mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Allow marking free functions as suffixes
This commit is contained in:
@@ -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.");
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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())),
|
||||
|
||||
@@ -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")),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user