[TMP] Replace IdentifierPath with Identifier/Member access for literal suffixes

This commit is contained in:
Kamil Śliwak 2022-07-27 12:19:02 +02:00
parent a5a294053d
commit d98b12420b
26 changed files with 129 additions and 70 deletions

View File

@ -234,10 +234,8 @@ bool FunctionCallGraphBuilder::visit(NewExpression const& _newExpression)
bool FunctionCallGraphBuilder::visit(Literal const& _literal)
{
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
functionReferenced(
dynamic_cast<FunctionDefinition const&>(*(*identifierPath)->annotation().referencedDeclaration)
);
if (_literal.suffixFunction())
functionReferenced(*_literal.suffixFunction());
return true;
}

View File

@ -3709,6 +3709,7 @@ void TypeChecker::endVisit(Literal const& _literal)
}
Literal::SubDenomination const* subDenomination = get_if<Literal::SubDenomination>(&_literal.suffix());
if (_literal.isHexNumber() && subDenomination && *subDenomination != Literal::SubDenomination::None)
m_errorReporter.fatalTypeError(
5145_error,
@ -3733,14 +3734,21 @@ void TypeChecker::endVisit(Literal const& _literal)
// TODO at this point 'type' needs to be stored for code generation.
bool isCompileTimeConstant = true;
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
if (!subDenomination)
{
Declaration const* declaration = (*identifierPath)->annotation().referencedDeclaration;
FunctionDefinition const* definition = dynamic_cast<FunctionDefinition const*>(declaration);
FunctionType const* suffixFunctionType = dynamic_cast<FunctionType const*>(std::visit(GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { return _identifier->annotation().type; },
[&](ASTPointer<MemberAccess> const& _memberAccess) { return _memberAccess->annotation().type; },
[&](Literal::SubDenomination) -> Type const* { solAssert(false); },
}, _literal.suffix()));
if (
!definition ||
!definition->isFree() ||
definition->stateMutability() != StateMutability::Pure
!suffixFunctionType || // Rejects variables
!suffixFunctionType->hasDeclaration() || // Rejects function pointers
!dynamic_cast<FunctionDefinition const*>(&suffixFunctionType->declaration()) || // Rejects events and errors
suffixFunctionType->bound() ||
!_literal.suffixFunction()->isFree() ||
_literal.suffixFunction()->stateMutability() != StateMutability::Pure
)
m_errorReporter.typeError(
4438_error,
@ -3749,24 +3757,23 @@ void TypeChecker::endVisit(Literal const& _literal)
);
else
{
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*declaration->type());
solAssert(!functionType.takesArbitraryParameters());
solAssert(functionType.kind() == FunctionType::Kind::Internal);
solAssert(!suffixFunctionType->takesArbitraryParameters());
solAssert(suffixFunctionType->kind() == FunctionType::Kind::Internal);
auto const* rationalType = dynamic_cast<RationalNumberType const*>(type);
auto const* literalRationalType = dynamic_cast<RationalNumberType const*>(type);
optional<string> parameterCountMessage;
if (functionType.parameterTypes().size() == 0)
if (suffixFunctionType->parameterTypes().size() == 0)
parameterCountMessage = "Functions that take no arguments cannot be used as literal suffixes.";
else if (functionType.parameterTypes().size() >= 3)
else if (suffixFunctionType->parameterTypes().size() >= 3)
parameterCountMessage = "Functions that take 3 or more arguments cannot be used as literal suffixes.";
else if (functionType.parameterTypes().size() == 2 && !rationalType)
else if (suffixFunctionType->parameterTypes().size() == 2 && !literalRationalType)
parameterCountMessage = "Functions that take 2 arguments can only be used as literal suffixes for rational numbers.";
optional<string> parameterTypeMessage;
if (parameterCountMessage.has_value())
m_errorReporter.typeError(4778_error, _literal.location(), parameterCountMessage.value());
else if (functionType.parameterTypes().size() == 2)
else if (suffixFunctionType->parameterTypes().size() == 2)
{
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
solAssert((mantissa && exponent) || (!mantissa && !exponent));
@ -3778,25 +3785,27 @@ void TypeChecker::endVisit(Literal const& _literal)
"that fit the range of parameters of the suffix function."
);
else if (
!mantissa->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(0)) ||
!exponent->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(1))
!mantissa->isImplicitlyConvertibleTo(*suffixFunctionType->parameterTypes().at(0)) ||
!exponent->isImplicitlyConvertibleTo(*suffixFunctionType->parameterTypes().at(1))
)
// TODO: Is this triggered when the argument is out of range? Test.
parameterTypeMessage = "The type of the literal cannot be converted to the parameters of the suffix function.";
}
else if (!type->isImplicitlyConvertibleTo(*functionType.parameterTypes().front()))
else if (!type->isImplicitlyConvertibleTo(*suffixFunctionType->parameterTypes().front()))
parameterTypeMessage = "The type of the literal cannot be converted to the parameter of the suffix function.";
if (parameterTypeMessage.has_value())
m_errorReporter.typeError(8838_error, _literal.location(), parameterTypeMessage.value());
isCompileTimeConstant = functionType.isPure();
if (functionType.returnParameterTypes().size() == 1)
type = functionType.returnParameterTypes().front();
isCompileTimeConstant = suffixFunctionType->isPure();
if (suffixFunctionType->returnParameterTypes().size() == 1)
type = suffixFunctionType->returnParameterTypes().front();
else
type = TypeProvider::tuple(functionType.returnParameterTypes());
type = TypeProvider::tuple(suffixFunctionType->returnParameterTypes());
}
}
else
solAssert(holds_alternative<Literal::SubDenomination>(_literal.suffix()));
_literal.annotation().type = type;
_literal.annotation().isPure = isCompileTimeConstant;

View File

@ -27,7 +27,9 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/AST_accept.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolutil/Keccak256.h>
#include <libsolutil/Visitor.h>
#include <boost/algorithm/string.hpp>
@ -940,6 +942,23 @@ ASTString Literal::valueWithoutUnderscores() const
return boost::erase_all_copy(value(), "_");
}
FunctionDefinition const* Literal::suffixFunction() const
{
if (holds_alternative<SubDenomination>(m_suffix))
return nullptr;
Declaration const* referencedDeclaration = visit(util::GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { return _identifier->annotation().referencedDeclaration; },
[&](ASTPointer<MemberAccess> 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<FunctionDefinition const*>(referencedDeclaration);
solAssert(functionDefinition, "Non-denomination literal suffix must be a function.");
return functionDefinition;
}
bool Literal::isHexNumber() const
{
if (token() != Token::Number)

View File

@ -2351,7 +2351,7 @@ public:
Week = static_cast<int>(Token::SubWeek),
Year = static_cast<int>(Token::SubYear)
};
using Suffix = std::variant<SubDenomination, ASTPointer<IdentifierPath>>;
using Suffix = std::variant<SubDenomination, ASTPointer<Identifier>, ASTPointer<MemberAccess>>;
Literal(
int64_t _id,
SourceLocation const& _location,
@ -2371,6 +2371,9 @@ public:
//SubDenomination subDenomination() const { return m_suffix; }
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;
/// @returns true if this is a number with a hex prefix.
bool isHexNumber() const;

View File

@ -27,6 +27,8 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolutil/Visitor.h>
namespace solidity::frontend
{
@ -1015,16 +1017,23 @@ void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
void Literal::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
(*identifierPath)->accept(_visitor);
std::visit(solidity::util::GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { _identifier->accept(_visitor); },
[&](ASTPointer<MemberAccess> const& _memberAccess) { _memberAccess->accept(_visitor); },
[&](SubDenomination) {},
}, m_suffix);
_visitor.endVisit(*this);
}
void Literal::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
(*identifierPath)->accept(_visitor);
std::visit(solidity::util::GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) { _identifier->accept(_visitor); },
[&](ASTPointer<MemberAccess> const& _memberAccess) { _memberAccess->accept(_visitor); },
[&](SubDenomination) {},
}, m_suffix);
_visitor.endVisit(*this);
}

View File

@ -2271,12 +2271,9 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
{
CompilerContext::LocationSetter locationSetter(m_context, _literal);
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
if (_literal.suffixDefinition())
{
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
*(*identifierPath)->annotation().referencedDeclaration
);
FunctionType const& functionType = *function.functionType(true);
FunctionType const& functionType = *_literal.suffixDefinition()->functionType(true);
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
@ -2298,7 +2295,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
m_context << type->literalValue(&_literal);
utils().convertType(*type, *functionType.parameterTypes().at(0));
}
m_context << m_context.functionEntryLabel(function).pushTag();
m_context << m_context.functionEntryLabel(*_literal.suffixDefinition()).pushTag();
m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
m_context << returnLabel;
@ -2311,6 +2308,8 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
}
else
{
solAssert(holds_alternative<Literal::SubDenomination>(_literal.suffix()));
Type const* type = _literal.annotation().type;

View File

@ -2438,12 +2438,9 @@ bool IRGeneratorForStatements::visit(Literal const& _literal)
{
setLocation(_literal);
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
if (_literal.suffixFunction())
{
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
*(*identifierPath)->annotation().referencedDeclaration
);
FunctionType const& functionType = *function.functionType(true);
FunctionType const& functionType = *_literal.suffixFunction()->functionType(true);
// TODO this is actually not always the right one.
auto type = TypeProvider::forLiteral(_literal);
@ -2472,11 +2469,12 @@ bool IRGeneratorForStatements::visit(Literal const& _literal)
// TODO what about string?
}
define(_literal) <<
m_context.enqueueFunctionForCodeGeneration(function) <<
m_context.enqueueFunctionForCodeGeneration(*_literal.suffixFunction()) <<
"(" + joinHumanReadable(args) + ")\n";
}
else
{
solAssert(holds_alternative<Literal::SubDenomination>(_literal.suffix()));
Type const& literalType = type(_literal);
switch (literalType.category())

View File

@ -35,10 +35,12 @@
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <cctype>
#include <vector>
#include <memory>
#include <regex>
#include <tuple>
#include <vector>
using namespace std;
using namespace solidity::langutil;
@ -1996,9 +1998,31 @@ ASTPointer<Expression> Parser::parseLiteral()
}
else if (m_scanner->currentToken() == Token::Identifier)
{
auto identifierPath = parseIdentifierPath();
nodeFactory.setEndPositionFromNode(identifierPath);
suffix = move(identifierPath);
// TODO: Make sure locations are set correctly
ASTPointer<ASTString> suffixName = make_shared<ASTString>(m_scanner->currentLiteral());
nodeFactory.markEndPosition();
ASTPointer<Identifier> identifier = nodeFactory.createNode<Identifier>(suffixName);
advance();
if (m_scanner->currentToken() != Token::Period)
suffix = identifier;
else
{
ASTPointer<Expression> memberAccess = identifier;
do
{
// FIXME: This grabs the semicolon
advance();
nodeFactory.markEndPosition();
SourceLocation memberLocation = currentLocation();
ASTPointer<ASTString> memberName = expectIdentifierToken();
memberAccess = nodeFactory.createNode<MemberAccess>(memberAccess, move(memberName), move(memberLocation));
}
while (m_scanner->currentToken() == Token::Period);
suffix = dynamic_pointer_cast<MemberAccess>(memberAccess);
}
}
return nodeFactory.createNode<Literal>(initialToken, move(value), move(suffix));
}

View File

@ -10,4 +10,4 @@ contract C {
int d = "a" suffix; // TODO: Should match only string
}
// ----
// DeclarationError 7920: (216-222): Identifier not found or not unique.
// TypeError 2144: (214-222): No matching declaration found after variable lookup.

View File

@ -18,4 +18,4 @@ contract C {
int f = -2.55 iuSuffix; // TODO: Should match only (uint8, uint)
}
// ----
// DeclarationError 7920: (347-354): Identifier not found or not unique.
// TypeError 2144: (341-354): No matching declaration found after variable lookup.

View File

@ -2,7 +2,7 @@ function iuSuffix(uint8, uint) pure returns (uint) {}
function iuSuffix(int8, uint) pure returns (uint) {}
contract C {
uint a = 1.27 iuSuffix;
uint a = 1.27 iuSuffix; // TODO: Error should say it's ambiguous
}
// ----
// DeclarationError 7920: (139-147): Identifier not found or not unique.
// TypeError 2144: (134-147): No matching declaration found after variable lookup.

View File

@ -2,7 +2,7 @@ function uSuffix(uint8, uint) pure returns (uint) {}
function uSuffix(uint16, uint) pure returns (uint) {}
contract C {
uint a = 1.27 uSuffix;
uint a = 1.27 uSuffix; // TODO: Error should say it's ambiguous
}
// ----
// DeclarationError 7920: (139-146): Identifier not found or not unique.
// TypeError 2144: (134-146): No matching declaration found after variable lookup.

View File

@ -11,4 +11,4 @@ contract C {
}
}
// ----
// DeclarationError 7920: (263-272): Identifier not found or not unique.
// TypeError 2144: (259-272): No matching declaration found after variable lookup.

View File

@ -3,8 +3,8 @@ function suffix(uint, uint) pure returns (int) {}
contract C {
function f() public pure {
int a = 1 suffix;
int a = 1 suffix; // TODO: Error should say it's ambiguous
}
}
// ----
// DeclarationError 7920: (157-163): Identifier not found or not unique.
// TypeError 2144: (155-163): No matching declaration found after variable lookup.

View File

@ -18,4 +18,4 @@ contract C {
int f = -255 iuSuffix; // TODO: Should match only uint8
}
// ----
// DeclarationError 7920: (310-317): Identifier not found or not unique.
// TypeError 2144: (305-317): No matching declaration found after variable lookup.

View File

@ -2,7 +2,7 @@ function iuSuffix(uint8) pure returns (int) {}
function iuSuffix(int8) pure returns (int) {}
contract C {
int a = 127 iuSuffix;
int a = 127 iuSuffix; // TODO: Error should say it's ambiguous
}
// ----
// DeclarationError 7920: (123-131): Identifier not found or not unique.
// TypeError 2144: (119-131): No matching declaration found after variable lookup.

View File

@ -2,7 +2,7 @@ function uSuffix(uint8) pure returns (int) {}
function uSuffix(uint16) pure returns (int) {}
contract C {
int a = 127 uSuffix;
int a = 127 uSuffix; // TODO: Error should say it's ambiguous
}
// ----
// DeclarationError 7920: (123-130): Identifier not found or not unique.
// TypeError 2144: (119-130): No matching declaration found after variable lookup.

View File

@ -5,4 +5,4 @@ contract C {
int a = hex"abcd" suffix; // TODO: Should match only bytes
}
// ----
// DeclarationError 7920: (141-147): Identifier not found or not unique.
// TypeError 2144: (131-147): No matching declaration found after variable lookup.

View File

@ -2,7 +2,7 @@ function suffix(string memory) pure returns (uint) {}
function suffix(bytes memory) pure returns (uint) {}
contract C {
uint a = "abcd" suffix;
uint a = "abcd" suffix; // TODO: Error should say it's ambiguous
}
// ----
// DeclarationError 7920: (141-147): Identifier not found or not unique.
// TypeError 2144: (134-147): No matching declaration found after variable lookup.

View File

@ -7,4 +7,4 @@ contract C {
uint b = 1000 a.suffix;
}
// ----
// DeclarationError 7920: (149-157): Identifier not found or not unique.
// TypeError 4438: (144-157): The literal suffix needs to be a pre-defined suffix or a file-level pure function.

View File

@ -9,4 +9,4 @@ contract C {
uint b = 1000 a.suffix;
}
// ----
// DeclarationError 7920: (169-177): Identifier not found or not unique.
// TypeError 4438: (164-177): The literal suffix needs to be a pre-defined suffix or a file-level pure function.

View File

@ -4,4 +4,4 @@ contract C {
function suffix(uint x) external pure returns (uint) { return x; }
}
// ----
// DeclarationError 7920: (31-42): Identifier not found or not unique.
// TypeError 4438: (26-42): The literal suffix needs to be a pre-defined suffix or a file-level pure function.

View File

@ -6,4 +6,4 @@ contract C {
uint x = 1000 L.suffix;
}
// ----
// DeclarationError 7920: (117-125): Identifier not found or not unique.
// TypeError 4438: (112-125): The literal suffix needs to be a pre-defined suffix or a file-level pure function.

View File

@ -4,4 +4,4 @@ contract C {
uint a = 1 this.g;
}
// ----
// DeclarationError 7920: (48-54): Identifier not found or not unique.
// TypeError 4438: (46-54): The literal suffix needs to be a pre-defined suffix or a file-level pure function.

View File

@ -4,4 +4,4 @@ contract C {
}
}
// ----
// DeclarationError 7920: (54-60): Identifier not found or not unique.
// TypeError 2144: (52-60): No matching declaration found after variable lookup.

View File

@ -8,4 +8,4 @@ contract C is B {
function suffix(uint x) internal pure override returns (uint) { return x; }
}
// ----
// DeclarationError 7920: (127-133): Identifier not found or not unique.
// TypeError 4438: (122-133): The literal suffix needs to be a pre-defined suffix or a file-level pure function.