Type::sameTypeOrPointerTo()

This commit is contained in:
Kamil Śliwak 2022-11-23 17:40:39 +01:00
parent acf943005a
commit 5657515f45
2 changed files with 44 additions and 15 deletions

View File

@ -282,6 +282,30 @@ string Type::identifier() const
return ret; return ret;
} }
bool Type::sameTypeOrPointerTo(Type const& _targetType, bool _excludeLocation) const
{
auto const* thisAsReference = dynamic_cast<ReferenceType const*>(this);
auto const* targetAsReference = dynamic_cast<ReferenceType const*>(&_targetType);
auto thisLocation = DataLocation::Storage;
auto targetLocation = DataLocation::Storage;
if (thisAsReference && !_excludeLocation)
thisLocation = thisAsReference->location();
if (targetAsReference && !_excludeLocation)
targetLocation = targetAsReference->location();
if (
(!targetAsReference || !targetAsReference->isPointer()) &&
(thisAsReference && thisAsReference->isPointer())
)
return false; // The target is a pointer to our type. We only accept the reverse.
return
// Compare as if both were pointers.
*TypeProvider::withLocationIfReference(thisLocation, this, true /* _isPointer */) ==
*TypeProvider::withLocationIfReference(targetLocation, &_targetType, true /* _isPointer */);
}
Type const* Type::commonType(Type const* _a, Type const* _b) Type const* Type::commonType(Type const* _a, Type const* _b)
{ {
if (!_a || !_b) if (!_a || !_b)
@ -361,23 +385,19 @@ vector<UsingForDirective const*> usingForDirectivesForType(Type const& _type, AS
if (usingFor->global() && usingFor->typeName()) if (usingFor->global() && usingFor->typeName())
usingForDirectives.emplace_back(usingFor); usingForDirectives.emplace_back(usingFor);
// Normalise data location of type.
DataLocation typeLocation = DataLocation::Storage;
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
typeLocation = refType->location();
return usingForDirectives | ranges::views::filter([&](UsingForDirective const* _directive) -> bool { return usingForDirectives | ranges::views::filter([&](UsingForDirective const* _directive) -> bool {
// Convert both types to pointers for comparison to see if the `using for` directive applies. if (!_directive->typeName())
return true;
Type const& usingForType = *_directive->typeName()->annotation().type;
if (auto const* referenceType = dynamic_cast<ReferenceType const*>(&usingForType))
// NOTE: We want the type in 'using for' to represent both pointers and values.
// sameTypeOrPointerTo() gives us that behavior as long as usingForType.isPointer() is true.
solAssert(referenceType->isPointer());
// Note that at this point we don't yet know if the functions are actually usable with the type. // Note that at this point we don't yet know if the functions are actually usable with the type.
// `_type` may not be convertible to the function parameter type. // `_type` may not be convertible to the function parameter type.
return return _type.sameTypeOrPointerTo(usingForType, true /* _excludeLocation */);
!_directive->typeName() ||
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) ==
*TypeProvider::withLocationIfReference(
typeLocation,
_directive->typeName()->annotation().type,
true
);
}) | ranges::to<vector<UsingForDirective const*>>; }) | ranges::to<vector<UsingForDirective const*>>;
} }
@ -1636,7 +1656,7 @@ BoolResult ArrayType::isImplicitlyConvertibleTo(Type const& _convertTo) const
} }
else else
{ {
// Conversion to storage pointer or to memory, we de not copy element-for-element here, so // Conversion to storage pointer or to memory, we do not copy element-for-element here, so
// require that the base type is the same, not only convertible. // require that the base type is the same, not only convertible.
// This disallows assignment of nested dynamic arrays from storage to memory for now. // This disallows assignment of nested dynamic arrays from storage to memory for now.
if ( if (

View File

@ -221,6 +221,15 @@ public:
virtual bool operator==(Type const& _other) const { return category() == _other.category(); } virtual bool operator==(Type const& _other) const { return category() == _other.category(); }
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); } virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
/// Compares types, accepting both exact matches and pointers. The comparison is not
/// symmetric, i.e.. the type may be a pointer to the target type but not the other way around.
/// You can think of the target as the left-hand side of an assignment - you can assign a value
/// to a pointer (without making a copy) but not a pointer to a value.
/// @param _excludeLocation Compare reference types as if both had identical locations.
/// @returns true if both types are equal or the object is a reference type that can point at
/// values of @a _targetType.
bool sameTypeOrPointerTo(Type const& _targetType, bool _excludeLocation = false) const;
/// @returns number of bytes used by this type when encoded for CALL. Cannot be used for /// @returns number of bytes used by this type when encoded for CALL. Cannot be used for
/// dynamically encoded types. /// dynamically encoded types.
/// Always returns a value greater than zero and throws if the type cannot be encoded in calldata /// Always returns a value greater than zero and throws if the type cannot be encoded in calldata