mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
f24c9c7bde
@ -1,6 +1,7 @@
|
||||
### 0.7.1 (unreleased)
|
||||
|
||||
Language Features:
|
||||
* Allow function definitions outside of contracts, behaving much like internal library functions.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
@ -8,7 +8,7 @@
|
||||
grammar Solidity;
|
||||
|
||||
sourceUnit
|
||||
: (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ;
|
||||
: (pragmaDirective | importDirective | structDefinition | enumDefinition | functionDefinition | contractDefinition)* EOF ;
|
||||
|
||||
pragmaDirective
|
||||
: 'pragma' pragmaName ( ~';' )* ';' ;
|
||||
|
@ -18,7 +18,7 @@ if they are marked ``virtual``. For details, please see
|
||||
::
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.6.99 <0.8.0;
|
||||
pragma solidity >0.7.0 <0.8.0;
|
||||
|
||||
contract owned {
|
||||
constructor() { owner = msg.sender; }
|
||||
|
@ -6,6 +6,34 @@
|
||||
Functions
|
||||
*********
|
||||
|
||||
Functions can be defined inside and outside of contracts.
|
||||
|
||||
Functions outside of a contract, also called "free functions", always have implicit ``internal``
|
||||
:ref:`visibility<visibility-and-getters>`. Their code is included in all contracts
|
||||
that call them, similar to internal library functions.
|
||||
|
||||
::
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0 <0.8.0;
|
||||
|
||||
function sum(uint[] memory _arr) pure returns (uint s) {
|
||||
for (uint i = 0; i < _arr.length; i++)
|
||||
s += _arr[i];
|
||||
}
|
||||
|
||||
contract ArrayExample {
|
||||
bool found;
|
||||
function f(uint[] memory _arr) public {
|
||||
// This calls the free function internally.
|
||||
// The compiler will add its code to the contract.
|
||||
uint s = sum(_arr);
|
||||
require(s >= 10);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.. _function-parameters-return-variables:
|
||||
|
||||
Function Parameters and Return Variables
|
||||
|
@ -5,7 +5,7 @@ Layout of a Solidity Source File
|
||||
Source files can contain an arbitrary number of
|
||||
:ref:`contract definitions<contract_structure>`, import_ directives,
|
||||
:ref:`pragma directives<pragma>` and
|
||||
:ref:`struct<structs>` and :ref:`enum<enums>` definitions.
|
||||
:ref:`struct<structs>`, :ref:`enum<enums>` and :ref:`function<functions>` definitions.
|
||||
|
||||
.. index:: ! license, spdx
|
||||
|
||||
|
@ -43,12 +43,14 @@ visibility.
|
||||
Functions
|
||||
=========
|
||||
|
||||
Functions are the executable units of code within a contract.
|
||||
Functions are the executable units of code. Functions are usually
|
||||
defined inside a contract, but they can also be defined outside of
|
||||
contracts.
|
||||
|
||||
::
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.8.0;
|
||||
pragma solidity >0.7.0 <0.8.0;
|
||||
|
||||
contract SimpleAuction {
|
||||
function bid() public payable { // Function
|
||||
@ -56,6 +58,11 @@ Functions are the executable units of code within a contract.
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function defined outside of a contract
|
||||
function helper(uint x) pure returns (uint) {
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
:ref:`function-calls` can happen internally or externally
|
||||
and have different levels of :ref:`visibility<visibility-and-getters>`
|
||||
towards other contracts. :ref:`Functions<functions>` accept :ref:`parameters and return variables<function-parameters-return-variables>` to pass parameters
|
||||
|
@ -134,15 +134,26 @@ vector<Declaration const*> GlobalContext::declarations() const
|
||||
MagicVariableDeclaration const* GlobalContext::currentThis() const
|
||||
{
|
||||
if (!m_thisPointer[m_currentContract])
|
||||
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(magicVariableToID("this"), "this", TypeProvider::contract(*m_currentContract));
|
||||
{
|
||||
Type const* type = TypeProvider::emptyTuple();
|
||||
if (m_currentContract)
|
||||
type = TypeProvider::contract(*m_currentContract);
|
||||
m_thisPointer[m_currentContract] =
|
||||
make_shared<MagicVariableDeclaration>(magicVariableToID("this"), "this", type);
|
||||
}
|
||||
return m_thisPointer[m_currentContract].get();
|
||||
|
||||
}
|
||||
|
||||
MagicVariableDeclaration const* GlobalContext::currentSuper() const
|
||||
{
|
||||
if (!m_superPointer[m_currentContract])
|
||||
m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(magicVariableToID("super"), "super", TypeProvider::contract(*m_currentContract, true));
|
||||
{
|
||||
Type const* type = TypeProvider::emptyTuple();
|
||||
if (m_currentContract)
|
||||
type = TypeProvider::contract(*m_currentContract, true);
|
||||
m_superPointer[m_currentContract] =
|
||||
make_shared<MagicVariableDeclaration>(magicVariableToID("super"), "super", type);
|
||||
}
|
||||
return m_superPointer[m_currentContract].get();
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@ class GlobalContext: private boost::noncopyable
|
||||
public:
|
||||
GlobalContext();
|
||||
void setCurrentContract(ContractDefinition const& _contract);
|
||||
void resetCurrentContract() { m_currentContract = nullptr; }
|
||||
MagicVariableDeclaration const* currentThis() const;
|
||||
MagicVariableDeclaration const* currentSuper() const;
|
||||
|
||||
|
@ -275,6 +275,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
||||
if (!resolveNamesAndTypesInternal(*node, true))
|
||||
success = false;
|
||||
}
|
||||
|
||||
// make "this" and "super" invisible.
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
|
||||
m_globalContext.resetCurrentContract();
|
||||
|
||||
return success;
|
||||
}
|
||||
else
|
||||
@ -548,6 +554,10 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
|
||||
|
||||
void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
|
||||
{
|
||||
// make "this" and "super" invisible.
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
|
||||
m_globalContext.resetCurrentContract();
|
||||
m_currentContract = nullptr;
|
||||
closeCurrentScope();
|
||||
}
|
||||
|
@ -311,6 +311,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
||||
);
|
||||
}
|
||||
if (
|
||||
m_currentContract &&
|
||||
m_currentContract->isLibrary() &&
|
||||
functionType->kind() == FunctionType::Kind::DelegateCall &&
|
||||
functionType->declaration().scope() == m_currentContract
|
||||
|
@ -295,7 +295,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
|
||||
|
||||
bool SyntaxChecker::visit(ContractDefinition const& _contract)
|
||||
{
|
||||
m_isInterface = _contract.isInterface();
|
||||
m_currentContractKind = _contract.contractKind();
|
||||
|
||||
ASTString const& contractName = _contract.name();
|
||||
for (FunctionDefinition const* function: _contract.definedFunctions())
|
||||
@ -309,19 +309,41 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
|
||||
return true;
|
||||
}
|
||||
|
||||
void SyntaxChecker::endVisit(ContractDefinition const&)
|
||||
{
|
||||
m_currentContractKind = std::nullopt;
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
if (!_function.isConstructor() && _function.noVisibilitySpecified())
|
||||
solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), "");
|
||||
|
||||
if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified())
|
||||
{
|
||||
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
|
||||
string suggestedVisibility =
|
||||
_function.isFallback() ||
|
||||
_function.isReceive() ||
|
||||
m_currentContractKind == ContractKind::Interface
|
||||
? "external" : "public";
|
||||
m_errorReporter.syntaxError(
|
||||
4937_error,
|
||||
_function.location(),
|
||||
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
|
||||
);
|
||||
}
|
||||
else if (_function.isFree())
|
||||
{
|
||||
if (!_function.noVisibilitySpecified())
|
||||
m_errorReporter.syntaxError(
|
||||
4126_error,
|
||||
_function.location(),
|
||||
"Free functions cannot have visibility."
|
||||
);
|
||||
if (!_function.isImplemented())
|
||||
m_errorReporter.typeError(4668_error, _function.location(), "Free functions must be implemented.");
|
||||
}
|
||||
|
||||
if (m_isInterface && !_function.modifiers().empty())
|
||||
if (m_currentContractKind == ContractKind::Interface && !_function.modifiers().empty())
|
||||
m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers.");
|
||||
else if (!_function.isImplemented() && !_function.modifiers().empty())
|
||||
m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers.");
|
||||
|
@ -83,6 +83,7 @@ private:
|
||||
bool visit(PlaceholderStatement const& _placeholderStatement) override;
|
||||
|
||||
bool visit(ContractDefinition const& _contract) override;
|
||||
void endVisit(ContractDefinition const& _contract) override;
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
bool visit(FunctionTypeName const& _node) override;
|
||||
|
||||
@ -102,7 +103,7 @@ private:
|
||||
bool m_versionPragmaFound = false;
|
||||
|
||||
int m_inLoopDepth = 0;
|
||||
bool m_isInterface = false;
|
||||
std::optional<ContractKind> m_currentContractKind;
|
||||
|
||||
SourceUnit const* m_sourceUnit = nullptr;
|
||||
};
|
||||
|
@ -271,6 +271,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
||||
{
|
||||
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
||||
solAssert(base, "Base contract not available.");
|
||||
solAssert(m_currentContract, "");
|
||||
|
||||
if (m_currentContract->isInterface() && !base->isInterface())
|
||||
m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces.");
|
||||
@ -327,7 +328,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
if (_function.markedVirtual())
|
||||
{
|
||||
if (_function.isConstructor())
|
||||
if (_function.isFree())
|
||||
m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual.");
|
||||
else if (_function.isConstructor())
|
||||
m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual.");
|
||||
else if (_function.annotation().contract->isInterface())
|
||||
m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\"");
|
||||
@ -336,12 +339,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
else if (_function.libraryFunction())
|
||||
m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\".");
|
||||
}
|
||||
if (_function.overrides() && _function.isFree())
|
||||
m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override.");
|
||||
|
||||
if (_function.isPayable())
|
||||
{
|
||||
if (_function.libraryFunction())
|
||||
m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable.");
|
||||
if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
|
||||
else if (_function.isFree())
|
||||
m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable.");
|
||||
else if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
|
||||
m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable.");
|
||||
}
|
||||
|
||||
@ -415,9 +422,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
set<Declaration const*> modifiers;
|
||||
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
|
||||
{
|
||||
auto baseContracts = dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts;
|
||||
vector<ContractDefinition const*> baseContracts;
|
||||
if (auto contract = dynamic_cast<ContractDefinition const*>(_function.scope()))
|
||||
{
|
||||
baseContracts = contract->annotation().linearizedBaseContracts;
|
||||
// Delete first base which is just the main contract itself
|
||||
baseContracts.erase(baseContracts.begin());
|
||||
}
|
||||
|
||||
visitManually(
|
||||
*modifier,
|
||||
@ -432,7 +443,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
else
|
||||
modifiers.insert(decl);
|
||||
}
|
||||
if (m_currentContract->isInterface())
|
||||
|
||||
solAssert(_function.isFree() == !m_currentContract, "");
|
||||
if (!m_currentContract)
|
||||
{
|
||||
solAssert(!_function.isConstructor(), "");
|
||||
solAssert(!_function.isFallback(), "");
|
||||
solAssert(!_function.isReceive(), "");
|
||||
}
|
||||
else if (m_currentContract->isInterface())
|
||||
{
|
||||
if (_function.isImplemented())
|
||||
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation.");
|
||||
@ -445,6 +464,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
else if (m_currentContract->contractKind() == ContractKind::Library)
|
||||
if (_function.isConstructor())
|
||||
m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries.");
|
||||
|
||||
if (_function.isImplemented())
|
||||
_function.body().accept(*this);
|
||||
else if (_function.isConstructor())
|
||||
@ -452,7 +472,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
else if (_function.libraryFunction())
|
||||
m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared.");
|
||||
else if (!_function.virtualSemantics())
|
||||
{
|
||||
if (_function.isFree())
|
||||
solAssert(m_errorReporter.hasErrors(), "");
|
||||
else
|
||||
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
|
||||
}
|
||||
|
||||
|
||||
if (_function.isFallback())
|
||||
@ -1726,7 +1751,9 @@ void TypeChecker::typeCheckFunctionCall(
|
||||
|
||||
if (_functionType->kind() == FunctionType::Kind::Declaration)
|
||||
{
|
||||
solAssert(_functionType->declaration().annotation().contract, "");
|
||||
if (
|
||||
m_currentContract &&
|
||||
m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) &&
|
||||
!dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented()
|
||||
)
|
||||
@ -2454,7 +2481,11 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
||||
if (contract->abstract())
|
||||
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);
|
||||
solAssert(
|
||||
!contract->annotation().linearizedBaseContracts.empty(),
|
||||
@ -2466,6 +2497,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
||||
_newExpression.location(),
|
||||
"Circular reference for contract creation (cannot create instance of derived or same contract)."
|
||||
);
|
||||
}
|
||||
|
||||
_newExpression.annotation().type = FunctionType::newExpressionType(*contract);
|
||||
}
|
||||
@ -2507,7 +2539,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
|
||||
// Retrieve the types of the arguments if this is used to call a function.
|
||||
auto const& arguments = _memberAccess.annotation().arguments;
|
||||
MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName);
|
||||
MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName);
|
||||
size_t const initialMemberCount = possibleMembers.size();
|
||||
if (initialMemberCount > 1 && arguments)
|
||||
{
|
||||
@ -2533,7 +2565,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
DataLocation::Storage,
|
||||
exprType
|
||||
);
|
||||
if (!storageType->members(m_currentContract).membersByName(memberName).empty())
|
||||
if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty())
|
||||
m_errorReporter.fatalTypeError(
|
||||
4994_error,
|
||||
_memberAccess.location(),
|
||||
@ -2691,8 +2723,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
annotation.isPure = true;
|
||||
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
|
||||
m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
|
||||
|
||||
if (
|
||||
memberName == "runtimeCode" &&
|
||||
!accessedContractType.immutableVariables().empty()
|
||||
@ -2703,6 +2733,14 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
"\"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))
|
||||
m_errorReporter.typeError(
|
||||
4224_error,
|
||||
@ -2710,6 +2748,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
"Circular reference for contract code access."
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
|
||||
annotation.isPure = true;
|
||||
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
|
||||
|
@ -167,6 +167,16 @@ private:
|
||||
|
||||
bool experimentalFeatureActive(ExperimentalFeature _feature) const;
|
||||
|
||||
/// @returns the current scope that can have function or type definitions.
|
||||
/// This is either a contract or a source unit.
|
||||
ASTNode const* currentDefinitionScope() const
|
||||
{
|
||||
if (m_currentContract)
|
||||
return m_currentContract;
|
||||
else
|
||||
return m_currentSourceUnit;
|
||||
}
|
||||
|
||||
SourceUnit const* m_currentSourceUnit = nullptr;
|
||||
ContractDefinition const* m_currentContract = nullptr;
|
||||
|
||||
|
@ -128,28 +128,12 @@ private:
|
||||
|
||||
bool ViewPureChecker::check()
|
||||
{
|
||||
vector<ContractDefinition const*> contracts;
|
||||
|
||||
for (auto const& node: m_ast)
|
||||
{
|
||||
SourceUnit const* source = dynamic_cast<SourceUnit const*>(node.get());
|
||||
solAssert(source, "");
|
||||
contracts += source->filteredNodes<ContractDefinition>(source->nodes());
|
||||
}
|
||||
|
||||
// Check modifiers first to infer their state mutability.
|
||||
for (auto const& contract: contracts)
|
||||
for (ModifierDefinition const* mod: contract->functionModifiers())
|
||||
mod->accept(*this);
|
||||
|
||||
for (auto const& contract: contracts)
|
||||
contract->accept(*this);
|
||||
for (auto const& source: m_ast)
|
||||
source->accept(*this);
|
||||
|
||||
return !m_errors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ViewPureChecker::visit(FunctionDefinition const& _funDef)
|
||||
{
|
||||
solAssert(!m_currentFunction, "");
|
||||
@ -307,6 +291,25 @@ void ViewPureChecker::reportMutability(
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
@ -429,8 +432,7 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier)
|
||||
solAssert(_modifier.name(), "");
|
||||
if (ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration))
|
||||
{
|
||||
solAssert(m_inferredMutability.count(mod), "");
|
||||
auto const& mutAndLocation = m_inferredMutability.at(mod);
|
||||
MutabilityAndLocation const& mutAndLocation = modifierMutability(*mod);
|
||||
reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location);
|
||||
}
|
||||
else
|
||||
|
@ -71,6 +71,9 @@ private:
|
||||
std::optional<langutil::SourceLocation> const& _nestedLocation = {}
|
||||
);
|
||||
|
||||
/// Determines the mutability of modifier if not already cached.
|
||||
MutabilityAndLocation const& modifierMutability(ModifierDefinition const& _modifier);
|
||||
|
||||
std::vector<std::shared_ptr<ASTNode>> const& m_ast;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
||||
|
@ -292,7 +292,7 @@ bool FunctionDefinition::libraryFunction() const
|
||||
Visibility FunctionDefinition::defaultVisibility() const
|
||||
{
|
||||
solAssert(!isConstructor(), "");
|
||||
return Declaration::defaultVisibility();
|
||||
return isFree() ? Visibility::Internal : Declaration::defaultVisibility();
|
||||
}
|
||||
|
||||
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
|
||||
@ -338,7 +338,7 @@ TypePointer FunctionDefinition::type() const
|
||||
|
||||
TypePointer FunctionDefinition::typeViaContractName() const
|
||||
{
|
||||
if (annotation().contract->isLibrary())
|
||||
if (libraryFunction())
|
||||
{
|
||||
if (isPublic())
|
||||
return FunctionType(*this).asExternallyCallableFunction(true);
|
||||
@ -374,7 +374,9 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual(
|
||||
if (_searchStart == nullptr && !virtualSemantics())
|
||||
return *this;
|
||||
|
||||
solAssert(!dynamic_cast<ContractDefinition const&>(*scope()).isLibrary(), "");
|
||||
solAssert(!isFree(), "");
|
||||
solAssert(isOrdinary(), "");
|
||||
solAssert(!libraryFunction(), "");
|
||||
|
||||
FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false);
|
||||
|
||||
@ -603,8 +605,7 @@ bool VariableDeclaration::isLibraryFunctionParameter() const
|
||||
if (!isCallableOrCatchParameter())
|
||||
return false;
|
||||
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
|
||||
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
|
||||
else
|
||||
return funDef->libraryFunction();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -774,6 +774,7 @@ public:
|
||||
ASTPointer<ASTString> const& _name,
|
||||
Visibility _visibility,
|
||||
StateMutability _stateMutability,
|
||||
bool _free,
|
||||
Token _kind,
|
||||
bool _isVirtual,
|
||||
ASTPointer<OverrideSpecifier> const& _overrides,
|
||||
@ -787,6 +788,7 @@ public:
|
||||
StructurallyDocumented(_documentation),
|
||||
ImplementationOptional(_body != nullptr),
|
||||
m_stateMutability(_stateMutability),
|
||||
m_free(_free),
|
||||
m_kind(_kind),
|
||||
m_functionModifiers(std::move(_modifiers)),
|
||||
m_body(_body)
|
||||
@ -804,6 +806,7 @@ public:
|
||||
bool isConstructor() const { return m_kind == Token::Constructor; }
|
||||
bool isFallback() const { return m_kind == Token::Fallback; }
|
||||
bool isReceive() const { return m_kind == Token::Receive; }
|
||||
bool isFree() const { return m_free; }
|
||||
Token kind() const { return m_kind; }
|
||||
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
|
||||
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
|
||||
@ -815,6 +818,7 @@ public:
|
||||
}
|
||||
bool isVisibleViaContractTypeAccess() const override
|
||||
{
|
||||
solAssert(!isFree(), "");
|
||||
return isOrdinary() && visibility() >= Visibility::Public;
|
||||
}
|
||||
bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); }
|
||||
@ -850,6 +854,7 @@ public:
|
||||
|
||||
private:
|
||||
StateMutability m_stateMutability;
|
||||
bool m_free;
|
||||
Token const m_kind;
|
||||
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
|
||||
ASTPointer<Block> m_body;
|
||||
|
@ -236,9 +236,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
|
||||
{
|
||||
/// Referenced declaration, set during reference resolution stage.
|
||||
Declaration const* referencedDeclaration = nullptr;
|
||||
/// Stores a reference to the current contract.
|
||||
/// This is needed because types of base contracts change depending on the context.
|
||||
ContractDefinition const* contractScope = nullptr;
|
||||
};
|
||||
|
||||
struct ExpressionAnnotation: ASTAnnotation
|
||||
|
@ -360,7 +360,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
||||
std::vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair("name", _node.name()),
|
||||
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
|
||||
make_pair("kind", TokenTraits::toString(_node.kind())),
|
||||
make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())),
|
||||
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
|
||||
make_pair("visibility", Declaration::visibilityToString(visibility)),
|
||||
make_pair("virtual", _node.markedVirtual()),
|
||||
@ -467,7 +467,6 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
|
||||
setJsonNode(_node, "UserDefinedTypeName", {
|
||||
make_pair("name", namePathToString(_node.namePath())),
|
||||
make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
|
||||
make_pair("contractScope", idOrNull(_node.annotation().contractScope)),
|
||||
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
|
||||
});
|
||||
return false;
|
||||
|
@ -381,6 +381,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
|
||||
astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!");
|
||||
|
||||
Token kind;
|
||||
bool freeFunction = false;
|
||||
string kindStr = member(_node, "kind").asString();
|
||||
|
||||
if (kindStr == "constructor")
|
||||
@ -391,17 +392,27 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
|
||||
kind = Token::Fallback;
|
||||
else if (kindStr == "receive")
|
||||
kind = Token::Receive;
|
||||
else if (kindStr == "freeFunction")
|
||||
{
|
||||
kind = Token::Function;
|
||||
freeFunction = true;
|
||||
}
|
||||
else
|
||||
astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]");
|
||||
|
||||
std::vector<ASTPointer<ModifierInvocation>> modifiers;
|
||||
for (auto& mod: member(_node, "modifiers"))
|
||||
modifiers.push_back(createModifierInvocation(mod));
|
||||
|
||||
Visibility vis = Visibility::Default;
|
||||
if (!freeFunction)
|
||||
vis = visibility(_node);
|
||||
return createASTNode<FunctionDefinition>(
|
||||
_node,
|
||||
memberAsASTString(_node, "name"),
|
||||
kind == Token::Constructor ? Visibility::Default : visibility(_node),
|
||||
vis,
|
||||
stateMutability(_node),
|
||||
freeFunction,
|
||||
kind,
|
||||
memberAsBool(_node, "virtual"),
|
||||
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
|
||||
|
@ -3151,10 +3151,8 @@ string FunctionType::toString(bool _short) const
|
||||
{
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration);
|
||||
solAssert(functionDefinition, "");
|
||||
auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope());
|
||||
solAssert(contract, "");
|
||||
name += contract->annotation().canonicalName;
|
||||
name += '.';
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope()))
|
||||
name += contract->annotation().canonicalName + ".";
|
||||
name += functionDefinition->name();
|
||||
}
|
||||
name += '(';
|
||||
@ -3275,7 +3273,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
|
||||
{
|
||||
// Note that m_declaration might also be a state variable!
|
||||
solAssert(m_declaration, "Declaration needed to determine interface function type.");
|
||||
bool isLibraryFunction = kind() != Kind::Event && dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
|
||||
bool isLibraryFunction = false;
|
||||
if (kind() != Kind::Event)
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope()))
|
||||
isLibraryFunction = contract->isLibrary();
|
||||
|
||||
util::Result<TypePointers> paramTypes =
|
||||
transformParametersToExternal(m_parameterTypes, isLibraryFunction);
|
||||
@ -3569,7 +3570,10 @@ string FunctionType::externalSignature() const
|
||||
}
|
||||
|
||||
// "inLibrary" is only relevant if this is not an event.
|
||||
bool const inLibrary = kind() != Kind::Event && dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
|
||||
bool inLibrary = false;
|
||||
if (kind() != Kind::Event)
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope()))
|
||||
inLibrary = contract->isLibrary();
|
||||
|
||||
auto extParams = transformParametersToExternal(m_parameterTypes, inLibrary);
|
||||
|
||||
|
@ -1736,9 +1736,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
Type::Category category = _memberAccess.annotation().type->category();
|
||||
solAssert(
|
||||
category == Type::Category::TypeType ||
|
||||
category == Type::Category::Module,
|
||||
category == Type::Category::Module ||
|
||||
category == Type::Category::Function,
|
||||
""
|
||||
);
|
||||
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||
{
|
||||
auto const* funDef = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
|
||||
solAssert(funDef && funDef->isFree(), "");
|
||||
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
|
||||
utils().pushCombinedFunctionEntryLabel(*funDef);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -107,8 +107,11 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
||||
case Token::Enum:
|
||||
nodes.push_back(parseEnumDefinition());
|
||||
break;
|
||||
case Token::Function:
|
||||
nodes.push_back(parseFunctionDefinition(true));
|
||||
break;
|
||||
default:
|
||||
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition.");
|
||||
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/function definition.");
|
||||
}
|
||||
}
|
||||
solAssert(m_recursionDepth == 0, "");
|
||||
@ -548,7 +551,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
||||
return result;
|
||||
}
|
||||
|
||||
ASTPointer<ASTNode> Parser::parseFunctionDefinition()
|
||||
ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
@ -607,6 +610,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
|
||||
name,
|
||||
header.visibility,
|
||||
header.stateMutability,
|
||||
_freeFunction,
|
||||
kind,
|
||||
header.isVirtual,
|
||||
header.overrides,
|
||||
|
@ -92,7 +92,7 @@ private:
|
||||
ASTPointer<OverrideSpecifier> parseOverrideSpecifier();
|
||||
StateMutability parseStateMutability();
|
||||
FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable);
|
||||
ASTPointer<ASTNode> parseFunctionDefinition();
|
||||
ASTPointer<ASTNode> parseFunctionDefinition(bool _freeFunction = false);
|
||||
ASTPointer<StructDefinition> parseStructDefinition();
|
||||
ASTPointer<EnumDefinition> parseEnumDefinition();
|
||||
ASTPointer<EnumValue> parseEnumValue();
|
||||
|
@ -1,4 +1,4 @@
|
||||
Error: Expected pragma, import directive or contract/interface/library/struct/enum definition.
|
||||
Error: Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
||||
--> recovery_ast_empty_contract/input.sol:3:1:
|
||||
|
|
||||
3 | c
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
// ----
|
@ -1,3 +1,3 @@
|
||||
unexpected
|
||||
// ----
|
||||
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum definition.
|
||||
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
||||
|
Loading…
Reference in New Issue
Block a user