From e06e2c72ce20360f4fb5e42442df48cc0c2b582a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 24 Feb 2023 19:47:00 +0100 Subject: [PATCH] User-defined literal suffixes: AST --- libsolidity/ast/AST.cpp | 78 ++++++++++++++++++++++++++++- libsolidity/ast/AST.h | 27 ++++++++-- libsolidity/ast/ASTAnnotations.h | 4 ++ libsolidity/ast/ASTJsonExporter.cpp | 12 ++--- libsolidity/ast/AST_accept.h | 17 ++++++- 5 files changed, 124 insertions(+), 14 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index fab1a20f8..898bff48b 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -986,11 +987,85 @@ IdentifierAnnotation& Identifier::annotation() const return initAnnotation(); } +Type const& Literal::typeOfValue() const +{ + // no suffix/denomination -> literal is the whole expression and types match. + // suffix -> type comes from return value of suffix function. + return std::visit(util::GenericVisitor{ + [](ASTPointer const& _identifier)-> Type const& { + solAssert(_identifier->annotation().arguments.has_value()); + solAssert(_identifier->annotation().arguments->numArguments() == 1); + solAssert(_identifier->annotation().arguments->types[0]); + return *_identifier->annotation().arguments->types[0]; + }, + [](ASTPointer const& _memberAccess)-> Type const& { + solAssert(_memberAccess->annotation().arguments.has_value()); + solAssert(_memberAccess->annotation().arguments->numArguments() == 1); + solAssert(_memberAccess->annotation().arguments->types[0]); + return *_memberAccess->annotation().arguments->types[0]; + }, + [&](Literal::SubDenomination) -> Type const& { + solAssert(annotation().type); + return *annotation().type; + }, + }, m_suffix); +} + ASTString Literal::valueWithoutUnderscores() const { return boost::erase_all_copy(value(), "_"); } +Literal::SubDenomination Literal::subDenomination() const +{ + solAssert(holds_alternative(m_suffix)); + return get(m_suffix); +} + +FunctionDefinition const* Literal::suffixFunction() const +{ + if (holds_alternative(m_suffix)) + return nullptr; + + Declaration const* referencedDeclaration = visit(util::GenericVisitor{ + [&](ASTPointer const& _identifier) { return _identifier->annotation().referencedDeclaration; }, + [&](ASTPointer const& _memberAccess) { return _memberAccess->annotation().referencedDeclaration; }, + [&](SubDenomination) -> Declaration const* { solAssert(false); }, + }, m_suffix); + + solAssert(referencedDeclaration, "Literal suffix must have a definition."); + auto const* functionDefinition = dynamic_cast(referencedDeclaration); + solAssert(functionDefinition, "Non-denomination literal suffix must be a function."); + return functionDefinition; +} + +FunctionType const* Literal::suffixFunctionType() const +{ + return dynamic_cast(visit(util::GenericVisitor{ + [&](ASTPointer const& _identifier) { return _identifier->annotation().type; }, + [&](ASTPointer const& _memberAccess) { return _memberAccess->annotation().type; }, + [&](Literal::SubDenomination) -> Type const* { return nullptr; }, + }, suffix())); +} + +bool Literal::isSuffixed() const +{ + return visit(util::GenericVisitor{ + [&](ASTPointer const&) { return true; }, + [&](ASTPointer const&) { return true; }, + [&](Literal::SubDenomination) { return hasSubDenomination(); }, + }, suffix()); +} + +bool Literal::hasSubDenomination() const +{ + return visit(util::GenericVisitor{ + [&](ASTPointer const&) { return false; }, + [&](ASTPointer const&) { return false; }, + [&](Literal::SubDenomination _subDenomination) { return _subDenomination != SubDenomination::None; }, + }, suffix()); +} + bool Literal::isHexNumber() const { if (token() != Token::Number) @@ -1000,7 +1075,8 @@ bool Literal::isHexNumber() const bool Literal::looksLikeAddress() const { - if (subDenomination() != SubDenomination::None) + // User suffixes are fine. + if (hasSubDenomination()) return false; if (!isHexNumber()) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 7060533d2..618d68be7 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -2377,6 +2377,8 @@ private: /** * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value. + * + * It can have a suffix that can lead to a function call. */ class Literal: public PrimaryExpression { @@ -2394,24 +2396,41 @@ public: Week = static_cast(Token::SubWeek), Year = static_cast(Token::SubYear) }; + using Suffix = std::variant, ASTPointer>; Literal( int64_t _id, SourceLocation const& _location, Token _token, ASTPointer _value, - SubDenomination _sub = SubDenomination::None + Suffix _suffix = SubDenomination::None ): - PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_subDenomination(_sub) {} + PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_suffix(std::move(_suffix)) {} void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; Token token() const { return m_token; } /// @returns the non-parsed value of the literal ASTString const& value() const { return *m_value; } + Type const& typeOfValue() const; ASTString valueWithoutUnderscores() const; - SubDenomination subDenomination() const { return m_subDenomination; } + /// @returns The subdenomination represented by the suffix. + /// Must not be called if the suffix is not a subdenomination. + SubDenomination subDenomination() const; + /// @returns The suffix of the literal. Does not check whether the suffix is present or is a + /// subdenomination. + /// @note Missing suffix is represented by SubDenomination::None. + Suffix const& suffix() const { return m_suffix; } + /// @returns Function definition associated with the suffix if the suffix is not a subdenomination. + /// nullptr otherwise. + FunctionDefinition const* suffixFunction() const; + FunctionType const* suffixFunctionType() const; + + /// @returns true if the literal is suffixed with either a subdenomination or a suffix function. + bool isSuffixed() const; + /// @returns true if the literal is suffixed a subdenomination. + bool hasSubDenomination() const; /// @returns true if this is a number with a hex prefix. bool isHexNumber() const; @@ -2426,7 +2445,7 @@ public: private: Token m_token; ASTPointer m_value; - SubDenomination m_subDenomination; + Suffix m_suffix; }; /// @} diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index b85eb3ad0..6547df459 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -302,6 +302,8 @@ struct IdentifierAnnotation: ExpressionAnnotation std::vector candidateDeclarations; /// List of possible declarations it could refer to. std::vector overloadedDeclarations; + /// If the identifier is used as a literal suffix, this points at the literal. + Literal const* suffixedLiteral = nullptr; }; struct MemberAccessAnnotation: ExpressionAnnotation @@ -310,6 +312,8 @@ struct MemberAccessAnnotation: ExpressionAnnotation Declaration const* referencedDeclaration = nullptr; /// What kind of lookup needs to be done (static, virtual, super) find the declaration. util::SetOnce requiredLookup; + /// If the expression is used as a literal suffix, this points at the literal. + Literal const* suffixedLiteral = nullptr; }; struct OperationAnnotation: ExpressionAnnotation diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index c1a537501..cbd71d87e 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -984,17 +984,15 @@ bool ASTJsonExporter::visit(Literal const& _node) Json::Value value{_node.value()}; if (!util::validateUTF8(_node.value())) value = Json::nullValue; - Token subdenomination = Token(_node.subDenomination()); + Json::Value subdenomination = Json::nullValue; +// if (auto subden = get_if(&_node.suffix())) +// subdenomination = Json::Value{TokenTraits::toString(*subden)}; + // TODO suffix std::vector> attributes = { make_pair("kind", literalTokenKind(_node.token())), make_pair("value", value), make_pair("hexValue", util::toHex(util::asBytes(_node.value()))), - make_pair( - "subdenomination", - subdenomination == Token::Illegal ? - Json::nullValue : - Json::Value{TokenTraits::toString(subdenomination)} - ) + make_pair("subdenomination", subdenomination) }; appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "Literal", std::move(attributes)); diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 78dc0e5dd..f4ce4baf3 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -27,6 +27,8 @@ #include #include +#include + namespace solidity::frontend { @@ -1014,13 +1016,24 @@ void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const void Literal::accept(ASTVisitor& _visitor) { - _visitor.visit(*this); + if (_visitor.visit(*this)) + std::visit(solidity::util::GenericVisitor{ + [&](ASTPointer const& _identifier) { _identifier->accept(_visitor); }, + [&](ASTPointer const& _memberAccess) { _memberAccess->accept(_visitor); }, + [&](SubDenomination) {}, + }, m_suffix); + _visitor.endVisit(*this); } void Literal::accept(ASTConstVisitor& _visitor) const { - _visitor.visit(*this); + if (_visitor.visit(*this)) + std::visit(solidity::util::GenericVisitor{ + [&](ASTPointer const& _identifier) { _identifier->accept(_visitor); }, + [&](ASTPointer const& _memberAccess) { _memberAccess->accept(_visitor); }, + [&](SubDenomination) {}, + }, m_suffix); _visitor.endVisit(*this); }