Merge pull request #8834 from ethereum/freeFunctions

Free functions.
This commit is contained in:
chriseth 2020-08-18 13:50:37 +02:00 committed by GitHub
commit f24c9c7bde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 525 additions and 86 deletions

View File

@ -1,6 +1,7 @@
### 0.7.1 (unreleased)
Language Features:
* Allow function definitions outside of contracts, behaving much like internal library functions.
Compiler Features:

View File

@ -8,7 +8,7 @@
grammar Solidity;
sourceUnit
: (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ;
: (pragmaDirective | importDirective | structDefinition | enumDefinition | functionDefinition | contractDefinition)* EOF ;
pragmaDirective
: 'pragma' pragmaName ( ~';' )* ';' ;

View File

@ -18,7 +18,7 @@ if they are marked ``virtual``. For details, please see
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.6.99 <0.8.0;
pragma solidity >0.7.0 <0.8.0;
contract owned {
constructor() { owner = msg.sender; }

View File

@ -6,6 +6,34 @@
Functions
*********
Functions can be defined inside and outside of contracts.
Functions outside of a contract, also called "free functions", always have implicit ``internal``
:ref:`visibility<visibility-and-getters>`. Their code is included in all contracts
that call them, similar to internal library functions.
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.7.0 <0.8.0;
function sum(uint[] memory _arr) pure returns (uint s) {
for (uint i = 0; i < _arr.length; i++)
s += _arr[i];
}
contract ArrayExample {
bool found;
function f(uint[] memory _arr) public {
// This calls the free function internally.
// The compiler will add its code to the contract.
uint s = sum(_arr);
require(s >= 10);
found = true;
}
}
.. _function-parameters-return-variables:
Function Parameters and Return Variables

View File

@ -5,7 +5,7 @@ Layout of a Solidity Source File
Source files can contain an arbitrary number of
:ref:`contract definitions<contract_structure>`, import_ directives,
:ref:`pragma directives<pragma>` and
:ref:`struct<structs>` and :ref:`enum<enums>` definitions.
:ref:`struct<structs>`, :ref:`enum<enums>` and :ref:`function<functions>` definitions.
.. index:: ! license, spdx

View File

@ -43,12 +43,14 @@ visibility.
Functions
=========
Functions are the executable units of code within a contract.
Functions are the executable units of code. Functions are usually
defined inside a contract, but they can also be defined outside of
contracts.
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.8.0;
pragma solidity >0.7.0 <0.8.0;
contract SimpleAuction {
function bid() public payable { // Function
@ -56,6 +58,11 @@ Functions are the executable units of code within a contract.
}
}
// Helper function defined outside of a contract
function helper(uint x) pure returns (uint) {
return x * 2;
}
:ref:`function-calls` can happen internally or externally
and have different levels of :ref:`visibility<visibility-and-getters>`
towards other contracts. :ref:`Functions<functions>` accept :ref:`parameters and return variables<function-parameters-return-variables>` to pass parameters

View File

@ -134,15 +134,26 @@ vector<Declaration const*> GlobalContext::declarations() const
MagicVariableDeclaration const* GlobalContext::currentThis() const
{
if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(magicVariableToID("this"), "this", TypeProvider::contract(*m_currentContract));
{
Type const* type = TypeProvider::emptyTuple();
if (m_currentContract)
type = TypeProvider::contract(*m_currentContract);
m_thisPointer[m_currentContract] =
make_shared<MagicVariableDeclaration>(magicVariableToID("this"), "this", type);
}
return m_thisPointer[m_currentContract].get();
}
MagicVariableDeclaration const* GlobalContext::currentSuper() const
{
if (!m_superPointer[m_currentContract])
m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(magicVariableToID("super"), "super", TypeProvider::contract(*m_currentContract, true));
{
Type const* type = TypeProvider::emptyTuple();
if (m_currentContract)
type = TypeProvider::contract(*m_currentContract, true);
m_superPointer[m_currentContract] =
make_shared<MagicVariableDeclaration>(magicVariableToID("super"), "super", type);
}
return m_superPointer[m_currentContract].get();
}

View File

@ -46,6 +46,7 @@ class GlobalContext: private boost::noncopyable
public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
void resetCurrentContract() { m_currentContract = nullptr; }
MagicVariableDeclaration const* currentThis() const;
MagicVariableDeclaration const* currentSuper() const;

View File

@ -275,6 +275,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
if (!resolveNamesAndTypesInternal(*node, true))
success = false;
}
// make "this" and "super" invisible.
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
m_globalContext.resetCurrentContract();
return success;
}
else
@ -548,6 +554,10 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
{
// make "this" and "super" invisible.
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
m_globalContext.resetCurrentContract();
m_currentContract = nullptr;
closeCurrentScope();
}

View File

@ -311,6 +311,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
);
}
if (
m_currentContract &&
m_currentContract->isLibrary() &&
functionType->kind() == FunctionType::Kind::DelegateCall &&
functionType->declaration().scope() == m_currentContract

View File

@ -295,7 +295,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
bool SyntaxChecker::visit(ContractDefinition const& _contract)
{
m_isInterface = _contract.isInterface();
m_currentContractKind = _contract.contractKind();
ASTString const& contractName = _contract.name();
for (FunctionDefinition const* function: _contract.definedFunctions())
@ -309,19 +309,41 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
return true;
}
void SyntaxChecker::endVisit(ContractDefinition const&)
{
m_currentContractKind = std::nullopt;
}
bool SyntaxChecker::visit(FunctionDefinition const& _function)
{
if (!_function.isConstructor() && _function.noVisibilitySpecified())
solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), "");
if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified())
{
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
string suggestedVisibility =
_function.isFallback() ||
_function.isReceive() ||
m_currentContractKind == ContractKind::Interface
? "external" : "public";
m_errorReporter.syntaxError(
4937_error,
_function.location(),
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
);
}
else if (_function.isFree())
{
if (!_function.noVisibilitySpecified())
m_errorReporter.syntaxError(
4126_error,
_function.location(),
"Free functions cannot have visibility."
);
if (!_function.isImplemented())
m_errorReporter.typeError(4668_error, _function.location(), "Free functions must be implemented.");
}
if (m_isInterface && !_function.modifiers().empty())
if (m_currentContractKind == ContractKind::Interface && !_function.modifiers().empty())
m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers.");
else if (!_function.isImplemented() && !_function.modifiers().empty())
m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers.");

View File

@ -83,6 +83,7 @@ private:
bool visit(PlaceholderStatement const& _placeholderStatement) override;
bool visit(ContractDefinition const& _contract) override;
void endVisit(ContractDefinition const& _contract) override;
bool visit(FunctionDefinition const& _function) override;
bool visit(FunctionTypeName const& _node) override;
@ -102,7 +103,7 @@ private:
bool m_versionPragmaFound = false;
int m_inLoopDepth = 0;
bool m_isInterface = false;
std::optional<ContractKind> m_currentContractKind;
SourceUnit const* m_sourceUnit = nullptr;
};

View File

@ -271,6 +271,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
{
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
solAssert(base, "Base contract not available.");
solAssert(m_currentContract, "");
if (m_currentContract->isInterface() && !base->isInterface())
m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces.");
@ -327,7 +328,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (_function.markedVirtual())
{
if (_function.isConstructor())
if (_function.isFree())
m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual.");
else if (_function.isConstructor())
m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual.");
else if (_function.annotation().contract->isInterface())
m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\"");
@ -336,12 +339,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else if (_function.libraryFunction())
m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\".");
}
if (_function.overrides() && _function.isFree())
m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override.");
if (_function.isPayable())
{
if (_function.libraryFunction())
m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable.");
if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
else if (_function.isFree())
m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable.");
else if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable.");
}
@ -415,9 +422,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
set<Declaration const*> modifiers;
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
{
auto baseContracts = dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts;
// Delete first base which is just the main contract itself
baseContracts.erase(baseContracts.begin());
vector<ContractDefinition const*> baseContracts;
if (auto contract = dynamic_cast<ContractDefinition const*>(_function.scope()))
{
baseContracts = contract->annotation().linearizedBaseContracts;
// Delete first base which is just the main contract itself
baseContracts.erase(baseContracts.begin());
}
visitManually(
*modifier,
@ -432,7 +443,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else
modifiers.insert(decl);
}
if (m_currentContract->isInterface())
solAssert(_function.isFree() == !m_currentContract, "");
if (!m_currentContract)
{
solAssert(!_function.isConstructor(), "");
solAssert(!_function.isFallback(), "");
solAssert(!_function.isReceive(), "");
}
else if (m_currentContract->isInterface())
{
if (_function.isImplemented())
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation.");
@ -445,6 +464,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else if (m_currentContract->contractKind() == ContractKind::Library)
if (_function.isConstructor())
m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries.");
if (_function.isImplemented())
_function.body().accept(*this);
else if (_function.isConstructor())
@ -452,7 +472,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else if (_function.libraryFunction())
m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared.");
else if (!_function.virtualSemantics())
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
{
if (_function.isFree())
solAssert(m_errorReporter.hasErrors(), "");
else
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
}
if (_function.isFallback())
@ -1726,7 +1751,9 @@ void TypeChecker::typeCheckFunctionCall(
if (_functionType->kind() == FunctionType::Kind::Declaration)
{
solAssert(_functionType->declaration().annotation().contract, "");
if (
m_currentContract &&
m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) &&
!dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented()
)
@ -2454,18 +2481,23 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (contract->abstract())
m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract.");
solAssert(!!m_currentContract, "");
m_currentContract->annotation().contractDependencies.insert(contract);
solAssert(
!contract->annotation().linearizedBaseContracts.empty(),
"Linearized base contracts not yet available."
);
if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError(
4579_error,
_newExpression.location(),
"Circular reference for contract creation (cannot create instance of derived or same contract)."
if (m_currentContract)
{
// TODO this is not properly detecting creation-cycles if they go through
// internal library functions or free functions. It will be caught at
// code generation time, but it would of course be better to catch it here.
m_currentContract->annotation().contractDependencies.insert(contract);
solAssert(
!contract->annotation().linearizedBaseContracts.empty(),
"Linearized base contracts not yet available."
);
if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError(
4579_error,
_newExpression.location(),
"Circular reference for contract creation (cannot create instance of derived or same contract)."
);
}
_newExpression.annotation().type = FunctionType::newExpressionType(*contract);
}
@ -2507,7 +2539,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
// Retrieve the types of the arguments if this is used to call a function.
auto const& arguments = _memberAccess.annotation().arguments;
MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName);
MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName);
size_t const initialMemberCount = possibleMembers.size();
if (initialMemberCount > 1 && arguments)
{
@ -2533,7 +2565,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
DataLocation::Storage,
exprType
);
if (!storageType->members(m_currentContract).membersByName(memberName).empty())
if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty())
m_errorReporter.fatalTypeError(
4994_error,
_memberAccess.location(),
@ -2691,8 +2723,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
{
annotation.isPure = true;
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
if (
memberName == "runtimeCode" &&
!accessedContractType.immutableVariables().empty()
@ -2703,12 +2733,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"\"runtimeCode\" is not available for contracts containing immutable variables."
);
if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError(
4224_error,
_memberAccess.location(),
"Circular reference for contract code access."
);
if (m_currentContract)
{
// TODO in the same way as with ``new``,
// this is not properly detecting creation-cycles if they go through
// internal library functions or free functions. It will be caught at
// code generation time, but it would of course be better to catch it here.
m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError(
4224_error,
_memberAccess.location(),
"Circular reference for contract code access."
);
}
}
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
annotation.isPure = true;

View File

@ -167,6 +167,16 @@ private:
bool experimentalFeatureActive(ExperimentalFeature _feature) const;
/// @returns the current scope that can have function or type definitions.
/// This is either a contract or a source unit.
ASTNode const* currentDefinitionScope() const
{
if (m_currentContract)
return m_currentContract;
else
return m_currentSourceUnit;
}
SourceUnit const* m_currentSourceUnit = nullptr;
ContractDefinition const* m_currentContract = nullptr;

View File

@ -128,28 +128,12 @@ private:
bool ViewPureChecker::check()
{
vector<ContractDefinition const*> contracts;
for (auto const& node: m_ast)
{
SourceUnit const* source = dynamic_cast<SourceUnit const*>(node.get());
solAssert(source, "");
contracts += source->filteredNodes<ContractDefinition>(source->nodes());
}
// Check modifiers first to infer their state mutability.
for (auto const& contract: contracts)
for (ModifierDefinition const* mod: contract->functionModifiers())
mod->accept(*this);
for (auto const& contract: contracts)
contract->accept(*this);
for (auto const& source: m_ast)
source->accept(*this);
return !m_errors;
}
bool ViewPureChecker::visit(FunctionDefinition const& _funDef)
{
solAssert(!m_currentFunction, "");
@ -304,7 +288,26 @@ void ViewPureChecker::reportMutability(
m_currentFunction->stateMutability() == StateMutability::Pure ||
m_currentFunction->stateMutability() == StateMutability::NonPayable,
""
);
);
}
ViewPureChecker::MutabilityAndLocation const& ViewPureChecker::modifierMutability(
ModifierDefinition const& _modifier
)
{
if (!m_inferredMutability.count(&_modifier))
{
MutabilityAndLocation bestMutabilityAndLocation{};
FunctionDefinition const* currentFunction = nullptr;
swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation);
swap(currentFunction, m_currentFunction);
_modifier.accept(*this);
swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation);
swap(currentFunction, m_currentFunction);
}
return m_inferredMutability.at(&_modifier);
}
void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
@ -429,8 +432,7 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier)
solAssert(_modifier.name(), "");
if (ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration))
{
solAssert(m_inferredMutability.count(mod), "");
auto const& mutAndLocation = m_inferredMutability.at(mod);
MutabilityAndLocation const& mutAndLocation = modifierMutability(*mod);
reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location);
}
else

View File

@ -71,6 +71,9 @@ private:
std::optional<langutil::SourceLocation> const& _nestedLocation = {}
);
/// Determines the mutability of modifier if not already cached.
MutabilityAndLocation const& modifierMutability(ModifierDefinition const& _modifier);
std::vector<std::shared_ptr<ASTNode>> const& m_ast;
langutil::ErrorReporter& m_errorReporter;

View File

@ -292,7 +292,7 @@ bool FunctionDefinition::libraryFunction() const
Visibility FunctionDefinition::defaultVisibility() const
{
solAssert(!isConstructor(), "");
return Declaration::defaultVisibility();
return isFree() ? Visibility::Internal : Declaration::defaultVisibility();
}
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
@ -338,7 +338,7 @@ TypePointer FunctionDefinition::type() const
TypePointer FunctionDefinition::typeViaContractName() const
{
if (annotation().contract->isLibrary())
if (libraryFunction())
{
if (isPublic())
return FunctionType(*this).asExternallyCallableFunction(true);
@ -374,7 +374,9 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(
if (_searchStart == nullptr && !virtualSemantics())
return *this;
solAssert(!dynamic_cast<ContractDefinition const&>(*scope()).isLibrary(), "");
solAssert(!isFree(), "");
solAssert(isOrdinary(), "");
solAssert(!libraryFunction(), "");
FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false);
@ -603,9 +605,8 @@ bool VariableDeclaration::isLibraryFunctionParameter() const
if (!isCallableOrCatchParameter())
return false;
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
else
return false;
return funDef->libraryFunction();
return false;
}
bool VariableDeclaration::isEventParameter() const

View File

@ -774,6 +774,7 @@ public:
ASTPointer<ASTString> const& _name,
Visibility _visibility,
StateMutability _stateMutability,
bool _free,
Token _kind,
bool _isVirtual,
ASTPointer<OverrideSpecifier> const& _overrides,
@ -787,6 +788,7 @@ public:
StructurallyDocumented(_documentation),
ImplementationOptional(_body != nullptr),
m_stateMutability(_stateMutability),
m_free(_free),
m_kind(_kind),
m_functionModifiers(std::move(_modifiers)),
m_body(_body)
@ -804,6 +806,7 @@ public:
bool isConstructor() const { return m_kind == Token::Constructor; }
bool isFallback() const { return m_kind == Token::Fallback; }
bool isReceive() const { return m_kind == Token::Receive; }
bool isFree() const { return m_free; }
Token kind() const { return m_kind; }
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
@ -815,6 +818,7 @@ public:
}
bool isVisibleViaContractTypeAccess() const override
{
solAssert(!isFree(), "");
return isOrdinary() && visibility() >= Visibility::Public;
}
bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); }
@ -850,6 +854,7 @@ public:
private:
StateMutability m_stateMutability;
bool m_free;
Token const m_kind;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<Block> m_body;

View File

@ -236,9 +236,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
{
/// Referenced declaration, set during reference resolution stage.
Declaration const* referencedDeclaration = nullptr;
/// Stores a reference to the current contract.
/// This is needed because types of base contracts change depending on the context.
ContractDefinition const* contractScope = nullptr;
};
struct ExpressionAnnotation: ASTAnnotation

View File

@ -360,7 +360,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("kind", TokenTraits::toString(_node.kind())),
make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())),
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
make_pair("visibility", Declaration::visibilityToString(visibility)),
make_pair("virtual", _node.markedVirtual()),
@ -467,7 +467,6 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
setJsonNode(_node, "UserDefinedTypeName", {
make_pair("name", namePathToString(_node.namePath())),
make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
make_pair("contractScope", idOrNull(_node.annotation().contractScope)),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
});
return false;

View File

@ -381,6 +381,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!");
Token kind;
bool freeFunction = false;
string kindStr = member(_node, "kind").asString();
if (kindStr == "constructor")
@ -391,17 +392,27 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
kind = Token::Fallback;
else if (kindStr == "receive")
kind = Token::Receive;
else if (kindStr == "freeFunction")
{
kind = Token::Function;
freeFunction = true;
}
else
astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]");
std::vector<ASTPointer<ModifierInvocation>> modifiers;
for (auto& mod: member(_node, "modifiers"))
modifiers.push_back(createModifierInvocation(mod));
Visibility vis = Visibility::Default;
if (!freeFunction)
vis = visibility(_node);
return createASTNode<FunctionDefinition>(
_node,
memberAsASTString(_node, "name"),
kind == Token::Constructor ? Visibility::Default : visibility(_node),
vis,
stateMutability(_node),
freeFunction,
kind,
memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),

View File

@ -3151,10 +3151,8 @@ string FunctionType::toString(bool _short) const
{
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration);
solAssert(functionDefinition, "");
auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope());
solAssert(contract, "");
name += contract->annotation().canonicalName;
name += '.';
if (auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope()))
name += contract->annotation().canonicalName + ".";
name += functionDefinition->name();
}
name += '(';
@ -3275,7 +3273,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
{
// Note that m_declaration might also be a state variable!
solAssert(m_declaration, "Declaration needed to determine interface function type.");
bool isLibraryFunction = kind() != Kind::Event && dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
bool isLibraryFunction = false;
if (kind() != Kind::Event)
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope()))
isLibraryFunction = contract->isLibrary();
util::Result<TypePointers> paramTypes =
transformParametersToExternal(m_parameterTypes, isLibraryFunction);
@ -3569,7 +3570,10 @@ string FunctionType::externalSignature() const
}
// "inLibrary" is only relevant if this is not an event.
bool const inLibrary = kind() != Kind::Event && dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
bool inLibrary = false;
if (kind() != Kind::Event)
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope()))
inLibrary = contract->isLibrary();
auto extParams = transformParametersToExternal(m_parameterTypes, inLibrary);

View File

@ -1736,9 +1736,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
Type::Category category = _memberAccess.annotation().type->category();
solAssert(
category == Type::Category::TypeType ||
category == Type::Category::Module,
category == Type::Category::Module ||
category == Type::Category::Function,
""
);
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
{
auto const* funDef = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
solAssert(funDef && funDef->isFree(), "");
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
utils().pushCombinedFunctionEntryLabel(*funDef);
}
break;
}
default:

View File

@ -107,8 +107,11 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
case Token::Enum:
nodes.push_back(parseEnumDefinition());
break;
case Token::Function:
nodes.push_back(parseFunctionDefinition(true));
break;
default:
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition.");
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/function definition.");
}
}
solAssert(m_recursionDepth == 0, "");
@ -548,7 +551,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
return result;
}
ASTPointer<ASTNode> Parser::parseFunctionDefinition()
ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
{
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
@ -607,6 +610,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
name,
header.visibility,
header.stateMutability,
_freeFunction,
kind,
header.isVirtual,
header.overrides,

View File

@ -92,7 +92,7 @@ private:
ASTPointer<OverrideSpecifier> parseOverrideSpecifier();
StateMutability parseStateMutability();
FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable);
ASTPointer<ASTNode> parseFunctionDefinition();
ASTPointer<ASTNode> parseFunctionDefinition(bool _freeFunction = false);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue();

View File

@ -1,4 +1,4 @@
Error: Expected pragma, import directive or contract/interface/library/struct/enum definition.
Error: Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
--> recovery_ast_empty_contract/input.sol:3:1:
|
3 | c

View File

@ -0,0 +1,13 @@
function add(uint a, uint b) pure returns (uint) {
return a + b;
}
contract C {
function f(uint x) public pure returns (uint) {
return add(x, 2);
}
}
// ====
// compileViaYul: also
// ----
// f(uint256): 7 -> 9

View File

@ -0,0 +1,15 @@
contract C {
uint public x = 2;
}
function test() returns (bool) {
return type(C).runtimeCode.length > 20;
}
contract D {
function f() public returns (bool) {
return test();
}
}
// ----
// f() -> true

View File

@ -0,0 +1,18 @@
==== Source: A ====
struct S { uint x; }
function set(S storage a, uint v) { a.x = v; }
==== Source: B ====
import "A";
import "A" as A;
contract C {
A.S data;
function f(uint v) public returns (uint one, uint two) {
A.set(data, v);
one = data.x;
set(data, v + 1);
two = data.x;
}
}
// ----
// f(uint256): 7 -> 7, 8

View File

@ -0,0 +1,21 @@
library L {
function pub() public pure returns (uint) {
return 7;
}
function inter() internal pure returns (uint) {
return 8;
}
}
function fu() pure returns (uint, uint) {
return (L.pub(), L.inter());
}
contract C {
function f() public pure returns (uint, uint) {
return fu();
}
}
// ----
// library: L
// f() -> 7, 8

View File

@ -0,0 +1,15 @@
contract C {
uint public x = 2;
}
function test() returns (uint) {
return (new C()).x();
}
contract D {
function f() public returns (uint) {
return test();
}
}
// ----
// f() -> 2

View File

@ -0,0 +1,16 @@
function f(uint) returns (uint) {
return 2;
}
function f(string memory) returns (uint) {
return 3;
}
contract C {
function g() public returns (uint, uint) {
return (f(2), f("abc"));
}
}
// ====
// compileViaYul: also
// ----
// g() -> 2, 3

View File

@ -0,0 +1,23 @@
function exp(uint base, uint exponent) pure returns (uint power) {
if (exponent == 0)
return 1;
power = exp(base, exponent / 2);
power *= power;
if (exponent & 1 == 1)
power *= base;
}
contract C {
function g(uint base, uint exponent) public pure returns (uint) {
return exp(base, exponent);
}
}
// ====
// compileViaYul: also
// ----
// g(uint256,uint256): 0, 0 -> 1
// g(uint256,uint256): 0, 1 -> 0x00
// g(uint256,uint256): 1, 0 -> 1
// g(uint256,uint256): 2, 3 -> 8
// g(uint256,uint256): 3, 10 -> 59049
// g(uint256,uint256): 2, 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968

View File

@ -0,0 +1,17 @@
contract C {
uint[] data;
function f(uint x, uint[] calldata input) public returns (uint, uint) {
data.push(x);
(uint a, uint[] calldata b) = fun(input, data);
return (a, b[1]);
}
}
function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) {
return (_y[0], _x);
}
// ====
// compileViaYul: also
// ----
// f(uint256,uint256[]): 7, 0x40, 3, 8, 9, 10 -> 7, 9

View File

@ -0,0 +1,8 @@
contract C {
function f() public pure {}
}
function fun() {
C.f();
}
// ----
// TypeError 3419: (68-73): Cannot call function via contract type name.

View File

@ -0,0 +1,3 @@
constructor() {}
// ----
// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.

View File

@ -0,0 +1,3 @@
fallback(){}
// ----
// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.

View File

@ -0,0 +1,4 @@
function fun() someModifier {
}
// ----
// DeclarationError 7576: (15-27): Undeclared identifier.

View File

@ -0,0 +1,9 @@
contract C {
modifier someModifier() { _; }
}
function fun() C.someModifier {
}
// ----
// ParserError 2314: (65-66): Expected '{' but got '.'

View File

@ -0,0 +1,9 @@
function f() {}
contract C {
function f() public {}
function g() public {
f();
}
}
// ----
// Warning 2519: (31-53): This declaration shadows an existing declaration.

View File

@ -0,0 +1,5 @@
function fun1() public { }
function fun2() internal { }
// ----
// SyntaxError 4126: (0-26): Free functions cannot have visibility.
// SyntaxError 4126: (27-55): Free functions cannot have visibility.

View File

@ -0,0 +1,3 @@
function f();
// ----
// TypeError 4668: (0-13): Free functions must be implemented.

View File

@ -0,0 +1,4 @@
function fun(uint256, uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) {
return (_y[0], _x);
}
// ----

View File

@ -0,0 +1,8 @@
function f() {
uint x = 2;
x;
}
function g(uint[] storage x) pure { x[0] = 1; }
// ----
// Warning 2018: (0-39): Function state mutability can be restricted to pure
// TypeError 8961: (76-80): Function declared as pure, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable.

View File

@ -0,0 +1,9 @@
function f(uint) returns (bytes memory) {}
function f(uint[] memory x) returns (bytes memory) { return f(x[0]); }
function g(uint8) {}
function g(uint16) {}
function t() {
g(2);
}
// ----
// TypeError 4487: (176-177): No unique declaration found after argument-dependent lookup.

View File

@ -0,0 +1,4 @@
function fun() override {
}
// ----
// SyntaxError 1750: (0-27): Free functions cannot override.

View File

@ -0,0 +1,4 @@
function fun() payable {
}
// ----
// TypeError 9559: (0-26): Free functions cannot be payable.

View File

@ -0,0 +1,3 @@
receive() {}
// ----
// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.

View File

@ -0,0 +1,4 @@
struct S { uint x; }
function fun(S storage) {
}
// ----

View File

@ -0,0 +1,4 @@
function fun() virtual {
}
// ----
// SyntaxError 4493: (0-26): Free functions cannot be virtual.

View File

@ -0,0 +1,4 @@
contract C {}
function C() {}
// ----
// DeclarationError 2333: (14-29): Identifier already declared.

View File

@ -0,0 +1,6 @@
contract C {
struct S { uint x; }
}
function f() returns (uint) { S storage t; }
// ----
// DeclarationError 7920: (70-71): Identifier not found or not unique.

View File

@ -0,0 +1,6 @@
function fun() {
fun{gas: 1}();
fun{value: 1}();
}
// ----
// TypeError 2193: (21-32): Function call options can only be set on external function calls or contract creations.

View File

@ -0,0 +1,7 @@
function f() returns (uint) { C.S storage t; t.x; }
contract C {
struct S { uint x; }
}
// ----
// TypeError 3464: (45-46): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour.

View File

@ -0,0 +1,3 @@
function f(S storage g) view returns (uint) { S storage t = g; return t.x; }
struct S { uint x; }
// ----

View File

@ -0,0 +1,6 @@
contract C {}
function f() {
super;
}
// ----
// DeclarationError 7576: (33-38): Undeclared identifier. "super" is not (or not yet) visible at this point.

View File

@ -0,0 +1,6 @@
contract C {}
function f() {
this;
}
// ----
// DeclarationError 7576: (33-37): Undeclared identifier. "this" is not (or not yet) visible at this point.

View File

@ -0,0 +1,14 @@
==== Source: a ====
function f(uint x) pure returns (uint) { return x * 3; }
==== Source: b ====
import "a" as A;
function g(uint x) pure returns (uint) { return A.f(x) * 3; }
==== Source: c ====
import "b" as B;
contract C {
function f() public pure {
B.g(2);
B.A.f(3);
}
}
// ----

View File

@ -1,3 +1,3 @@
unexpected
// ----
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum definition.
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.