Allow marking free functions as suffixes

This commit is contained in:
Kamil Śliwak
2023-04-12 12:07:46 +02:00
parent 9b000cc5d2
commit 9af3439ff7
95 changed files with 242 additions and 5 deletions
+98
View File
@@ -426,6 +426,104 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else if (_function.libraryFunction())
m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\".");
}
if (_function.usableAsSuffix())
{
if (_function.stateMutability() != StateMutability::Pure)
m_errorReporter.typeError(
1716_error,
_function.location(),
"Only pure functions can be used as literal suffixes"
);
optional<string> parameterCountMessage;
if (_function.parameterList().parameters().size() == 0)
parameterCountMessage = "Functions that take no arguments cannot be used as literal suffixes.";
else if (_function.parameterList().parameters().size() >= 3)
parameterCountMessage = "Functions that take 3 or more arguments cannot be used as literal suffixes.";
if (parameterCountMessage.has_value())
m_errorReporter.typeError(9128_error, _function.parameterList().location(), parameterCountMessage.value());
else if (_function.parameterList().parameters().size() == 2)
{
auto const* mantissaType = dynamic_cast<IntegerType const*>(_function.parameterList().parameters()[0]->type());
auto const* exponentType = dynamic_cast<IntegerType const*>(_function.parameterList().parameters()[1]->type());
vector<string> mantissaOrExponentTypeErrorMessages;
if (!mantissaType)
mantissaOrExponentTypeErrorMessages.emplace_back("The mantissa parameter must be an integer.");
if (!exponentType)
mantissaOrExponentTypeErrorMessages.emplace_back("The exponent parameter must be an unsigned integer.");
if (!mantissaOrExponentTypeErrorMessages.empty())
m_errorReporter.typeError(
1587_error,
_function.parameterList().location(),
"Literal suffix function has invalid parameter types. " +
joinHumanReadable(mantissaOrExponentTypeErrorMessages, " ")
);
if (exponentType && exponentType->isSigned())
m_errorReporter.typeError(
3123_error,
_function.parameterList().parameters()[1]->typeName().location(),
"The exponent parameter of a literal suffix function must be unsigned. "
"Exponent is always either zero or a negative power of 10 but the parameter represents its absolute value."
);
}
else if (_function.parameterList().parameters().size() == 1)
{
auto const* parameterType = _function.parameterList().parameters()[0]->type();
if (dynamic_cast<FixedPointType const*>(parameterType))
m_errorReporter.typeError(
2699_error,
_function.parameterList().parameters()[0]->location(),
"Parameters of fixed-point types are not allowed in literal suffix functions. "
"To support fractional literals the suffix function must accept two integer arguments "
"(mantissa and exponent) that such literals can be decomposed into."
);
if (
!TypeProvider::boolean()->isImplicitlyConvertibleTo(*parameterType) &&
// ASSUMPTION: There are no address payable literals.
!TypeProvider::address()->isImplicitlyConvertibleTo(*parameterType) &&
// ASSUMPTION: Literal 1 is implicitly convertible to any integer type.
!TypeProvider::rationalNumber(1)->isImplicitlyConvertibleTo(*parameterType) &&
// ASSUMPTION: bytes1 is implicitly convertible to any fixed-bytes type.
!TypeProvider::fixedBytes(1)->isImplicitlyConvertibleTo(*parameterType) &&
!TypeProvider::stringLiteral("a")->isImplicitlyConvertibleTo(*parameterType)
)
m_errorReporter.typeError(
2998_error,
_function.parameterList().parameters()[0]->location(),
"This literal suffix function is not usable as a suffix because no literal is "
"implicitly convertible to its parameter type."
);
}
solAssert(_function.returnParameterList());
if (_function.returnParameterList()->parameters().size() != 1)
{
m_errorReporter.typeError(
7848_error,
_function.returnParameterList()->location(),
"Literal suffix functions must return exactly one value."
);
}
for (ASTPointer<VariableDeclaration const> returnParameter: _function.returnParameterList()->parameters())
{
solAssert(returnParameter);
auto referenceType = dynamic_cast<ReferenceType const*>(returnParameter->type());
auto mappingType = dynamic_cast<MappingType const*>(returnParameter->type());
if (mappingType || (referenceType && !referenceType->dataStoredIn(DataLocation::Memory)))
m_errorReporter.typeError(
7251_error,
returnParameter->location(),
"Literal suffix functions can only return value types and reference types stored in memory."
);
}
}
if (_function.overrides() && _function.isFree())
m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override.");
+6
View File
@@ -936,6 +936,7 @@ public:
bool _free,
Token _kind,
bool _isVirtual,
bool _usableAsSuffix,
ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
@@ -949,11 +950,14 @@ public:
m_stateMutability(_stateMutability),
m_free(_free),
m_kind(_kind),
m_usableAsSuffix(_usableAsSuffix),
m_functionModifiers(std::move(_modifiers)),
m_body(_body)
{
solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, "");
solAssert(isOrdinary() == !name().empty(), "");
if (_usableAsSuffix)
solAssert(_free);
}
void accept(ASTVisitor& _visitor) override;
@@ -966,6 +970,7 @@ public:
bool isFallback() const { return m_kind == Token::Fallback; }
bool isReceive() const { return m_kind == Token::Receive; }
bool isFree() const { return m_free; }
bool usableAsSuffix() const { return m_usableAsSuffix; }
Token kind() const { return m_kind; }
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
@@ -1015,6 +1020,7 @@ private:
StateMutability m_stateMutability;
bool m_free;
Token const m_kind;
bool m_usableAsSuffix;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<Block> m_body;
};
+1
View File
@@ -444,6 +444,7 @@ bool ASTJsonExporter::visit(FunctionDefinition const& _node)
make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())),
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
make_pair("virtual", _node.markedVirtual()),
make_pair("suffix", _node.usableAsSuffix()),
make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue),
make_pair("parameters", toJson(_node.parameterList())),
make_pair("returnParameters", toJson(*_node.returnParameterList())),
+1
View File
@@ -552,6 +552,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
freeFunction,
kind,
memberAsBool(_node, "virtual"),
memberAsBool(_node, "suffix"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")),
+16 -4
View File
@@ -528,7 +528,7 @@ StateMutability Parser::parseStateMutability()
return stateMutability;
}
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVariable)
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVariable, bool _freeFunction)
{
RecursionGuard recursionGuard(*this);
FunctionHeaderParserResult result;
@@ -540,7 +540,18 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
{
Token token = m_scanner->currentToken();
if (!_isStateVariable && token == Token::Identifier)
result.modifiers.push_back(parseModifierInvocation());
{
if (_freeFunction && currentLiteral() == "suffix")
{
if (result.usableAsSuffix)
parserError(2878_error, "Suffix already specified.");
else
result.usableAsSuffix = true;
advance();
}
else
result.modifiers.push_back(parseModifierInvocation());
}
else if (TokenTraits::isVisibilitySpecifier(token))
{
if (result.visibility != Visibility::Default)
@@ -650,7 +661,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
name = make_shared<ASTString>();
}
FunctionHeaderParserResult header = parseFunctionHeader(false);
FunctionHeaderParserResult header = parseFunctionHeader(false /* _isStateVariable */, _freeFunction);
ASTPointer<Block> block;
nodeFactory.markEndPosition();
@@ -669,6 +680,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
_freeFunction,
kind,
header.isVirtual,
header.usableAsSuffix,
header.overrides,
documentation,
header.parameters,
@@ -1188,7 +1200,7 @@ ASTPointer<FunctionTypeName> Parser::parseFunctionType()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Function);
FunctionHeaderParserResult header = parseFunctionHeader(true);
FunctionHeaderParserResult header = parseFunctionHeader(true /* _isStateVariable */, false /* _freeFunction */);
return nodeFactory.createNode<FunctionTypeName>(
header.parameters,
header.returnParameters,
+2 -1
View File
@@ -69,6 +69,7 @@ private:
struct FunctionHeaderParserResult
{
bool isVirtual = false;
bool usableAsSuffix = false;
ASTPointer<OverrideSpecifier> overrides;
ASTPointer<ParameterList> parameters;
ASTPointer<ParameterList> returnParameters;
@@ -99,7 +100,7 @@ private:
Visibility parseVisibilitySpecifier();
ASTPointer<OverrideSpecifier> parseOverrideSpecifier();
StateMutability parseStateMutability();
FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable);
FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable, bool _freeFunction);
ASTPointer<ASTNode> parseFunctionDefinition(bool _freeFunction = false);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();