mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Free functions.
This commit is contained in:
parent
660ef792ab
commit
9324fb4f20
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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.");
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
if (auto contract = dynamic_cast<ContractDefinition const*>(_function.scope()))
|
||||||
|
{
|
||||||
|
baseContracts = contract->annotation().linearizedBaseContracts;
|
||||||
// Delete first base which is just the main contract itself
|
// Delete first base which is just the main contract itself
|
||||||
baseContracts.erase(baseContracts.begin());
|
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())
|
||||||
|
{
|
||||||
|
if (_function.isFree())
|
||||||
|
solAssert(m_errorReporter.hasErrors(), "");
|
||||||
|
else
|
||||||
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
|
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,7 +2481,11 @@ 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)
|
||||||
|
{
|
||||||
|
// 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);
|
m_currentContract->annotation().contractDependencies.insert(contract);
|
||||||
solAssert(
|
solAssert(
|
||||||
!contract->annotation().linearizedBaseContracts.empty(),
|
!contract->annotation().linearizedBaseContracts.empty(),
|
||||||
@ -2466,6 +2497,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
|||||||
_newExpression.location(),
|
_newExpression.location(),
|
||||||
"Circular reference for contract creation (cannot create instance of derived or same contract)."
|
"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,6 +2733,14 @@ 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 (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))
|
if (contractDependenciesAreCyclic(*m_currentContract))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
4224_error,
|
4224_error,
|
||||||
@ -2710,6 +2748,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
"Circular reference for contract code access."
|
"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;
|
||||||
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
|
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -128,30 +128,23 @@ private:
|
|||||||
|
|
||||||
bool ViewPureChecker::check()
|
bool ViewPureChecker::check()
|
||||||
{
|
{
|
||||||
vector<ContractDefinition const*> contracts;
|
// Process modifiers first to infer their state mutability.
|
||||||
|
m_checkModifiers = true;
|
||||||
|
for (auto const& source: m_ast)
|
||||||
|
source->accept(*this);
|
||||||
|
|
||||||
for (auto const& node: m_ast)
|
m_checkModifiers = false;
|
||||||
{
|
for (auto const& source: m_ast)
|
||||||
SourceUnit const* source = dynamic_cast<SourceUnit const*>(node.get());
|
source->accept(*this);
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
if (m_checkModifiers)
|
||||||
|
return false;
|
||||||
|
|
||||||
solAssert(!m_currentFunction, "");
|
solAssert(!m_currentFunction, "");
|
||||||
m_currentFunction = &_funDef;
|
m_currentFunction = &_funDef;
|
||||||
m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()};
|
m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()};
|
||||||
@ -160,6 +153,9 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef)
|
|||||||
|
|
||||||
void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
||||||
{
|
{
|
||||||
|
if (m_checkModifiers)
|
||||||
|
return;
|
||||||
|
|
||||||
solAssert(m_currentFunction == &_funDef, "");
|
solAssert(m_currentFunction == &_funDef, "");
|
||||||
if (
|
if (
|
||||||
m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
|
m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
|
||||||
@ -181,6 +177,9 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
|||||||
|
|
||||||
bool ViewPureChecker::visit(ModifierDefinition const& _modifier)
|
bool ViewPureChecker::visit(ModifierDefinition const& _modifier)
|
||||||
{
|
{
|
||||||
|
if (!m_checkModifiers)
|
||||||
|
return false;
|
||||||
|
|
||||||
solAssert(m_currentFunction == nullptr, "");
|
solAssert(m_currentFunction == nullptr, "");
|
||||||
m_bestMutabilityAndLocation = {StateMutability::Pure, _modifier.location()};
|
m_bestMutabilityAndLocation = {StateMutability::Pure, _modifier.location()};
|
||||||
return true;
|
return true;
|
||||||
@ -188,6 +187,9 @@ bool ViewPureChecker::visit(ModifierDefinition const& _modifier)
|
|||||||
|
|
||||||
void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef)
|
void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef)
|
||||||
{
|
{
|
||||||
|
if (!m_checkModifiers)
|
||||||
|
return;
|
||||||
|
|
||||||
solAssert(m_currentFunction == nullptr, "");
|
solAssert(m_currentFunction == nullptr, "");
|
||||||
m_inferredMutability[&_modifierDef] = std::move(m_bestMutabilityAndLocation);
|
m_inferredMutability[&_modifierDef] = std::move(m_bestMutabilityAndLocation);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ private:
|
|||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
|
|
||||||
bool m_errors = false;
|
bool m_errors = false;
|
||||||
|
bool m_checkModifiers = false;
|
||||||
MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()};
|
MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()};
|
||||||
FunctionDefinition const* m_currentFunction = nullptr;
|
FunctionDefinition const* m_currentFunction = nullptr;
|
||||||
std::map<ModifierDefinition const*, MutabilityAndLocation> m_inferredMutability;
|
std::map<ModifierDefinition const*, MutabilityAndLocation> m_inferredMutability;
|
||||||
|
@ -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,8 +605,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(); }
|
||||||
@ -848,8 +852,12 @@ public:
|
|||||||
ContractDefinition const* _searchStart = nullptr
|
ContractDefinition const* _searchStart = nullptr
|
||||||
) const override;
|
) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Visibility defaultVisibility() const override { return isFree() ? Visibility::Internal : Visibility::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;
|
||||||
|
@ -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
|
||||||
|
@ -361,6 +361,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
|||||||
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", TokenTraits::toString(_node.kind())),
|
||||||
|
make_pair("freeFunction", _node.isFree()),
|
||||||
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 +468,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;
|
||||||
|
@ -397,11 +397,25 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
|
|||||||
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;
|
||||||
|
bool const freeFunction = _node.isMember("freeFunction") ? _node["freeFunction"].asBool() : false;
|
||||||
|
if (kind == Token::Constructor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (freeFunction)
|
||||||
|
astAssert(
|
||||||
|
vis == Visibility::Internal || vis == Visibility::Default,
|
||||||
|
"Expected internal or default visibility for free function."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
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")),
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
13
test/libsolidity/semanticTests/freeFunctions/easy.sol
Normal file
13
test/libsolidity/semanticTests/freeFunctions/easy.sol
Normal 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
|
@ -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
|
18
test/libsolidity/semanticTests/freeFunctions/import.sol
Normal file
18
test/libsolidity/semanticTests/freeFunctions/import.sol
Normal 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
|
@ -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
|
@ -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
|
16
test/libsolidity/semanticTests/freeFunctions/overloads.sol
Normal file
16
test/libsolidity/semanticTests/freeFunctions/overloads.sol
Normal 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
|
23
test/libsolidity/semanticTests/freeFunctions/recursion.sol
Normal file
23
test/libsolidity/semanticTests/freeFunctions/recursion.sol
Normal 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
|
@ -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
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||||||
|
constructor() {}
|
||||||
|
// ----
|
||||||
|
// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
@ -0,0 +1,3 @@
|
|||||||
|
fallback(){}
|
||||||
|
// ----
|
||||||
|
// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
@ -0,0 +1,4 @@
|
|||||||
|
function fun() someModifier {
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 7576: (15-27): Undeclared identifier.
|
@ -0,0 +1,9 @@
|
|||||||
|
contract C {
|
||||||
|
modifier someModifier() { _; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function fun() C.someModifier {
|
||||||
|
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError 2314: (65-66): Expected '{' but got '.'
|
@ -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.
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||||||
|
function f();
|
||||||
|
// ----
|
||||||
|
// TypeError 4668: (0-13): Free functions must be implemented.
|
@ -0,0 +1,4 @@
|
|||||||
|
function fun(uint256, uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) {
|
||||||
|
return (_y[0], _x);
|
||||||
|
}
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -0,0 +1,4 @@
|
|||||||
|
function fun() override {
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// SyntaxError 1750: (0-27): Free functions cannot override.
|
@ -0,0 +1,4 @@
|
|||||||
|
function fun() payable {
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 9559: (0-26): Free functions cannot be payable.
|
@ -0,0 +1,3 @@
|
|||||||
|
receive() {}
|
||||||
|
// ----
|
||||||
|
// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
@ -0,0 +1,4 @@
|
|||||||
|
struct S { uint x; }
|
||||||
|
function fun(S storage) {
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,4 @@
|
|||||||
|
function fun() virtual {
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// SyntaxError 4493: (0-26): Free functions cannot be virtual.
|
@ -0,0 +1,4 @@
|
|||||||
|
contract C {}
|
||||||
|
function C() {}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 2333: (14-29): Identifier already declared.
|
@ -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.
|
6
test/libsolidity/syntaxTests/freeFunctions/gas_value.sol
Normal file
6
test/libsolidity/syntaxTests/freeFunctions/gas_value.sol
Normal 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.
|
@ -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.
|
@ -0,0 +1,3 @@
|
|||||||
|
function f(S storage g) view returns (uint) { S storage t = g; return t.x; }
|
||||||
|
struct S { uint x; }
|
||||||
|
// ----
|
@ -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.
|
@ -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.
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
Loading…
Reference in New Issue
Block a user