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) ### 0.7.1 (unreleased)
Language Features: Language Features:
* Allow function definitions outside of contracts, behaving much like internal library functions.
Compiler Features: Compiler Features:

View File

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

View File

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

View File

@ -6,6 +6,34 @@
Functions 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-return-variables:
Function Parameters and 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 Source files can contain an arbitrary number of
:ref:`contract definitions<contract_structure>`, import_ directives, :ref:`contract definitions<contract_structure>`, import_ directives,
:ref:`pragma directives<pragma>` and :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 .. index:: ! license, spdx

View File

@ -43,12 +43,14 @@ visibility.
Functions 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.8.0; pragma solidity >0.7.0 <0.8.0;
contract SimpleAuction { contract SimpleAuction {
function bid() public payable { // Function 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 :ref:`function-calls` can happen internally or externally
and have different levels of :ref:`visibility<visibility-and-getters>` 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 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 MagicVariableDeclaration const* GlobalContext::currentThis() const
{ {
if (!m_thisPointer[m_currentContract]) 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(); return m_thisPointer[m_currentContract].get();
} }
MagicVariableDeclaration const* GlobalContext::currentSuper() const MagicVariableDeclaration const* GlobalContext::currentSuper() const
{ {
if (!m_superPointer[m_currentContract]) 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(); return m_superPointer[m_currentContract].get();
} }

View File

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

View File

@ -275,6 +275,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
if (!resolveNamesAndTypesInternal(*node, true)) if (!resolveNamesAndTypesInternal(*node, true))
success = false; 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; return success;
} }
else else
@ -548,6 +554,10 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
void DeclarationRegistrationHelper::endVisit(ContractDefinition&) 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; m_currentContract = nullptr;
closeCurrentScope(); closeCurrentScope();
} }

View File

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

View File

@ -295,7 +295,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
bool SyntaxChecker::visit(ContractDefinition const& _contract) bool SyntaxChecker::visit(ContractDefinition const& _contract)
{ {
m_isInterface = _contract.isInterface(); m_currentContractKind = _contract.contractKind();
ASTString const& contractName = _contract.name(); ASTString const& contractName = _contract.name();
for (FunctionDefinition const* function: _contract.definedFunctions()) for (FunctionDefinition const* function: _contract.definedFunctions())
@ -309,19 +309,41 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
return true; return true;
} }
void SyntaxChecker::endVisit(ContractDefinition const&)
{
m_currentContractKind = std::nullopt;
}
bool SyntaxChecker::visit(FunctionDefinition const& _function) 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( m_errorReporter.syntaxError(
4937_error, 4937_error,
_function.location(), _function.location(),
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" "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."); m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers.");
else if (!_function.isImplemented() && !_function.modifiers().empty()) else if (!_function.isImplemented() && !_function.modifiers().empty())
m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers."); 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(PlaceholderStatement const& _placeholderStatement) override;
bool visit(ContractDefinition const& _contract) override; bool visit(ContractDefinition const& _contract) override;
void endVisit(ContractDefinition const& _contract) override;
bool visit(FunctionDefinition const& _function) override; bool visit(FunctionDefinition const& _function) override;
bool visit(FunctionTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override;
@ -102,7 +103,7 @@ private:
bool m_versionPragmaFound = false; bool m_versionPragmaFound = false;
int m_inLoopDepth = 0; int m_inLoopDepth = 0;
bool m_isInterface = false; std::optional<ContractKind> m_currentContractKind;
SourceUnit const* m_sourceUnit = nullptr; 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())); auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
solAssert(base, "Base contract not available."); solAssert(base, "Base contract not available.");
solAssert(m_currentContract, "");
if (m_currentContract->isInterface() && !base->isInterface()) if (m_currentContract->isInterface() && !base->isInterface())
m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); 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.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."); m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual.");
else if (_function.annotation().contract->isInterface()) else if (_function.annotation().contract->isInterface())
m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); 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()) else if (_function.libraryFunction())
m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); 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.isPayable())
{ {
if (_function.libraryFunction()) if (_function.libraryFunction())
m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); 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."); 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; set<Declaration const*> modifiers;
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers()) for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
{ {
auto baseContracts = dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts; vector<ContractDefinition const*> baseContracts;
// Delete first base which is just the main contract itself if (auto contract = dynamic_cast<ContractDefinition const*>(_function.scope()))
baseContracts.erase(baseContracts.begin()); {
baseContracts = contract->annotation().linearizedBaseContracts;
// Delete first base which is just the main contract itself
baseContracts.erase(baseContracts.begin());
}
visitManually( visitManually(
*modifier, *modifier,
@ -432,7 +443,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else else
modifiers.insert(decl); 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()) if (_function.isImplemented())
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); 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) else if (m_currentContract->contractKind() == ContractKind::Library)
if (_function.isConstructor()) if (_function.isConstructor())
m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries.");
if (_function.isImplemented()) if (_function.isImplemented())
_function.body().accept(*this); _function.body().accept(*this);
else if (_function.isConstructor()) else if (_function.isConstructor())
@ -452,7 +472,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else if (_function.libraryFunction()) else if (_function.libraryFunction())
m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared.");
else if (!_function.virtualSemantics()) 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()) if (_function.isFallback())
@ -1726,7 +1751,9 @@ void TypeChecker::typeCheckFunctionCall(
if (_functionType->kind() == FunctionType::Kind::Declaration) if (_functionType->kind() == FunctionType::Kind::Declaration)
{ {
solAssert(_functionType->declaration().annotation().contract, "");
if ( if (
m_currentContract &&
m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) && m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) &&
!dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented() !dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented()
) )
@ -2454,18 +2481,23 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (contract->abstract()) if (contract->abstract())
m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract.");
solAssert(!!m_currentContract, ""); if (m_currentContract)
m_currentContract->annotation().contractDependencies.insert(contract); {
solAssert( // TODO this is not properly detecting creation-cycles if they go through
!contract->annotation().linearizedBaseContracts.empty(), // internal library functions or free functions. It will be caught at
"Linearized base contracts not yet available." // code generation time, but it would of course be better to catch it here.
); m_currentContract->annotation().contractDependencies.insert(contract);
if (contractDependenciesAreCyclic(*m_currentContract)) solAssert(
m_errorReporter.typeError( !contract->annotation().linearizedBaseContracts.empty(),
4579_error, "Linearized base contracts not yet available."
_newExpression.location(),
"Circular reference for contract creation (cannot create instance of derived or same contract)."
); );
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); _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. // Retrieve the types of the arguments if this is used to call a function.
auto const& arguments = _memberAccess.annotation().arguments; 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(); size_t const initialMemberCount = possibleMembers.size();
if (initialMemberCount > 1 && arguments) if (initialMemberCount > 1 && arguments)
{ {
@ -2533,7 +2565,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
DataLocation::Storage, DataLocation::Storage,
exprType exprType
); );
if (!storageType->members(m_currentContract).membersByName(memberName).empty()) if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty())
m_errorReporter.fatalTypeError( m_errorReporter.fatalTypeError(
4994_error, 4994_error,
_memberAccess.location(), _memberAccess.location(),
@ -2691,8 +2723,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
{ {
annotation.isPure = true; annotation.isPure = true;
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument()); ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
if ( if (
memberName == "runtimeCode" && memberName == "runtimeCode" &&
!accessedContractType.immutableVariables().empty() !accessedContractType.immutableVariables().empty()
@ -2703,12 +2733,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"\"runtimeCode\" is not available for contracts containing immutable variables." "\"runtimeCode\" is not available for contracts containing immutable variables."
); );
if (contractDependenciesAreCyclic(*m_currentContract)) if (m_currentContract)
m_errorReporter.typeError( {
4224_error, // TODO in the same way as with ``new``,
_memberAccess.location(), // this is not properly detecting creation-cycles if they go through
"Circular reference for contract code access." // 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") else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
annotation.isPure = true; annotation.isPure = true;

View File

@ -167,6 +167,16 @@ private:
bool experimentalFeatureActive(ExperimentalFeature _feature) const; 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; SourceUnit const* m_currentSourceUnit = nullptr;
ContractDefinition const* m_currentContract = nullptr; ContractDefinition const* m_currentContract = nullptr;

View File

@ -128,28 +128,12 @@ private:
bool ViewPureChecker::check() bool ViewPureChecker::check()
{ {
vector<ContractDefinition const*> contracts; for (auto const& source: m_ast)
source->accept(*this);
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);
return !m_errors; return !m_errors;
} }
bool ViewPureChecker::visit(FunctionDefinition const& _funDef) bool ViewPureChecker::visit(FunctionDefinition const& _funDef)
{ {
solAssert(!m_currentFunction, ""); solAssert(!m_currentFunction, "");
@ -304,7 +288,26 @@ void ViewPureChecker::reportMutability(
m_currentFunction->stateMutability() == StateMutability::Pure || m_currentFunction->stateMutability() == StateMutability::Pure ||
m_currentFunction->stateMutability() == StateMutability::NonPayable, 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) void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
@ -429,8 +432,7 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier)
solAssert(_modifier.name(), ""); solAssert(_modifier.name(), "");
if (ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration)) if (ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration))
{ {
solAssert(m_inferredMutability.count(mod), ""); MutabilityAndLocation const& mutAndLocation = modifierMutability(*mod);
auto const& mutAndLocation = m_inferredMutability.at(mod);
reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location); reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location);
} }
else else

View File

@ -71,6 +71,9 @@ private:
std::optional<langutil::SourceLocation> const& _nestedLocation = {} 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; std::vector<std::shared_ptr<ASTNode>> const& m_ast;
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;

View File

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

View File

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

View File

@ -236,9 +236,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
{ {
/// Referenced declaration, set during reference resolution stage. /// Referenced declaration, set during reference resolution stage.
Declaration const* referencedDeclaration = nullptr; 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 struct ExpressionAnnotation: ASTAnnotation

View File

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

View File

@ -381,6 +381,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!"); astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!");
Token kind; Token kind;
bool freeFunction = false;
string kindStr = member(_node, "kind").asString(); string kindStr = member(_node, "kind").asString();
if (kindStr == "constructor") if (kindStr == "constructor")
@ -391,17 +392,27 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
kind = Token::Fallback; kind = Token::Fallback;
else if (kindStr == "receive") else if (kindStr == "receive")
kind = Token::Receive; kind = Token::Receive;
else if (kindStr == "freeFunction")
{
kind = Token::Function;
freeFunction = true;
}
else else
astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]"); astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]");
std::vector<ASTPointer<ModifierInvocation>> modifiers; std::vector<ASTPointer<ModifierInvocation>> modifiers;
for (auto& mod: member(_node, "modifiers")) for (auto& mod: member(_node, "modifiers"))
modifiers.push_back(createModifierInvocation(mod)); modifiers.push_back(createModifierInvocation(mod));
Visibility vis = Visibility::Default;
if (!freeFunction)
vis = visibility(_node);
return createASTNode<FunctionDefinition>( return createASTNode<FunctionDefinition>(
_node, _node,
memberAsASTString(_node, "name"), memberAsASTString(_node, "name"),
kind == Token::Constructor ? Visibility::Default : visibility(_node), vis,
stateMutability(_node), stateMutability(_node),
freeFunction,
kind, kind,
memberAsBool(_node, "virtual"), memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), _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); auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration);
solAssert(functionDefinition, ""); solAssert(functionDefinition, "");
auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope()); if (auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope()))
solAssert(contract, ""); name += contract->annotation().canonicalName + ".";
name += contract->annotation().canonicalName;
name += '.';
name += functionDefinition->name(); name += functionDefinition->name();
} }
name += '('; name += '(';
@ -3275,7 +3273,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
{ {
// Note that m_declaration might also be a state variable! // Note that m_declaration might also be a state variable!
solAssert(m_declaration, "Declaration needed to determine interface function type."); 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 = util::Result<TypePointers> paramTypes =
transformParametersToExternal(m_parameterTypes, isLibraryFunction); transformParametersToExternal(m_parameterTypes, isLibraryFunction);
@ -3569,7 +3570,10 @@ string FunctionType::externalSignature() const
} }
// "inLibrary" is only relevant if this is not an event. // "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); 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(); Type::Category category = _memberAccess.annotation().type->category();
solAssert( solAssert(
category == Type::Category::TypeType || 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; break;
} }
default: default:

View File

@ -107,8 +107,11 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
case Token::Enum: case Token::Enum:
nodes.push_back(parseEnumDefinition()); nodes.push_back(parseEnumDefinition());
break; break;
case Token::Function:
nodes.push_back(parseFunctionDefinition(true));
break;
default: 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, ""); solAssert(m_recursionDepth == 0, "");
@ -548,7 +551,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
return result; return result;
} }
ASTPointer<ASTNode> Parser::parseFunctionDefinition() ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
@ -607,6 +610,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
name, name,
header.visibility, header.visibility,
header.stateMutability, header.stateMutability,
_freeFunction,
kind, kind,
header.isVirtual, header.isVirtual,
header.overrides, header.overrides,

View File

@ -92,7 +92,7 @@ private:
ASTPointer<OverrideSpecifier> parseOverrideSpecifier(); ASTPointer<OverrideSpecifier> parseOverrideSpecifier();
StateMutability parseStateMutability(); StateMutability parseStateMutability();
FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable);
ASTPointer<ASTNode> parseFunctionDefinition(); ASTPointer<ASTNode> parseFunctionDefinition(bool _freeFunction = false);
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue(); 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: --> recovery_ast_empty_contract/input.sol:3:1:
| |
3 | c 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 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.