From 5657515f45f738a1fef701b1c552c14f847eb152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 23 Nov 2022 17:40:39 +0100 Subject: [PATCH] Type::sameTypeOrPointerTo() --- libsolidity/ast/Types.cpp | 50 +++++++++++++++++++++++++++------------ libsolidity/ast/Types.h | 9 +++++++ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 8f305669b..de180c8cc 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -282,6 +282,30 @@ string Type::identifier() const return ret; } +bool Type::sameTypeOrPointerTo(Type const& _targetType, bool _excludeLocation) const +{ + auto const* thisAsReference = dynamic_cast(this); + auto const* targetAsReference = dynamic_cast(&_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) { if (!_a || !_b) @@ -361,23 +385,19 @@ vector usingForDirectivesForType(Type const& _type, AS if (usingFor->global() && usingFor->typeName()) usingForDirectives.emplace_back(usingFor); - // Normalise data location of type. - DataLocation typeLocation = DataLocation::Storage; - if (auto refType = dynamic_cast(&_type)) - typeLocation = refType->location(); - 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(&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. // `_type` may not be convertible to the function parameter type. - return - !_directive->typeName() || - *TypeProvider::withLocationIfReference(typeLocation, &_type, true) == - *TypeProvider::withLocationIfReference( - typeLocation, - _directive->typeName()->annotation().type, - true - ); + return _type.sameTypeOrPointerTo(usingForType, true /* _excludeLocation */); }) | ranges::to>; } @@ -1636,7 +1656,7 @@ BoolResult ArrayType::isImplicitlyConvertibleTo(Type const& _convertTo) const } 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. // This disallows assignment of nested dynamic arrays from storage to memory for now. if ( diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index f240ee3c0..4472824d2 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -221,6 +221,15 @@ public: virtual bool operator==(Type const& _other) const { return category() == _other.category(); } 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 /// dynamically encoded types. /// Always returns a value greater than zero and throws if the type cannot be encoded in calldata