Merge pull request #12604 from ethereum/develop

Merge develop into breaking
This commit is contained in:
chriseth
2022-01-31 17:59:03 +01:00
committed by GitHub
206 changed files with 3675 additions and 1271 deletions
+24 -9
View File
@@ -25,19 +25,21 @@ using namespace solidity::langutil;
using namespace solidity::frontend;
using namespace std;
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow, ContractDefinition const* _contract):
m_nodeContainer(_nodeContainer),
m_currentNode(_functionFlow.entry),
m_returnNode(_functionFlow.exit),
m_revertNode(_functionFlow.revert),
m_transactionReturnNode(_functionFlow.transactionReturn)
m_transactionReturnNode(_functionFlow.transactionReturn),
m_contract(_contract)
{
}
unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
CFG::NodeContainer& _nodeContainer,
FunctionDefinition const& _function
FunctionDefinition const& _function,
ContractDefinition const* _contract
)
{
auto functionFlow = make_unique<FunctionFlow>();
@@ -45,7 +47,7 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
functionFlow->exit = _nodeContainer.newNode();
functionFlow->revert = _nodeContainer.newNode();
functionFlow->transactionReturn = _nodeContainer.newNode();
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
ControlFlowBuilder builder(_nodeContainer, *functionFlow, _contract);
builder.appendControlFlow(_function);
return functionFlow;
@@ -297,7 +299,8 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
_functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this);
m_currentNode->functionCalls.emplace_back(&_functionCall);
solAssert(!m_currentNode->functionCall);
m_currentNode->functionCall = &_functionCall;
auto nextNode = newLabel();
@@ -321,8 +324,20 @@ bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
_modifierInvocation.name().annotation().referencedDeclaration
);
if (!modifierDefinition) return false;
if (!modifierDefinition->isImplemented()) return false;
if (!modifierDefinition)
return false;
VirtualLookup const& requiredLookup = *_modifierInvocation.name().annotation().requiredLookup;
if (requiredLookup == VirtualLookup::Virtual)
modifierDefinition = &modifierDefinition->resolveVirtual(*m_contract);
else
solAssert(requiredLookup == VirtualLookup::Static);
if (!modifierDefinition->isImplemented())
return false;
solAssert(!!m_returnNode, "");
m_placeholderEntry = newLabel();
@@ -355,8 +370,8 @@ bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
}
for (auto const& modifier: _functionDefinition.modifiers())
appendControlFlow(*modifier);
for (auto const& modifierInvocation: _functionDefinition.modifiers())
appendControlFlow(*modifierInvocation);
appendControlFlow(_functionDefinition.body());
+6 -2
View File
@@ -37,13 +37,15 @@ class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker
public:
static std::unique_ptr<FunctionFlow> createFunctionFlow(
CFG::NodeContainer& _nodeContainer,
FunctionDefinition const& _function
FunctionDefinition const& _function,
ContractDefinition const* _contract = nullptr
);
private:
explicit ControlFlowBuilder(
CFG::NodeContainer& _nodeContainer,
FunctionFlow const& _functionFlow
FunctionFlow const& _functionFlow,
ContractDefinition const* _contract = nullptr
);
// Visits for constructing the control flow.
@@ -158,6 +160,8 @@ private:
CFGNode* m_revertNode = nullptr;
CFGNode* m_transactionReturnNode = nullptr;
ContractDefinition const* m_contract = nullptr;
/// The current jump destination of break Statements.
CFGNode* m_breakJump = nullptr;
/// The current jump destination of continue Statements.
+1 -1
View File
@@ -44,7 +44,7 @@ bool CFG::visit(ContractDefinition const& _contract)
for (FunctionDefinition const* function: contract->definedFunctions())
if (function->isImplemented())
m_functionControlFlow[{&_contract, function}] =
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function);
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function, &_contract);
return true;
}
+2 -2
View File
@@ -98,8 +98,8 @@ struct CFGNode
std::vector<CFGNode*> entries;
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
std::vector<CFGNode*> exits;
/// Function calls done by this node
std::vector<FunctionCall const*> functionCalls;
/// Function call done by this node
FunctionCall const* functionCall = nullptr;
/// Variable occurrences in the node.
std::vector<VariableOccurrence> variableOccurrences;
@@ -81,27 +81,27 @@ void ControlFlowRevertPruner::findRevertStates()
if (_node == functionFlow.exit)
foundExit = true;
for (auto const* functionCall: _node->functionCalls)
if (auto const* functionCall = _node->functionCall)
{
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.contract);
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
continue;
CFG::FunctionContractTuple calledFunctionTuple{
findScopeContract(*resolvedFunction, item.contract),
resolvedFunction
};
switch (m_functions.at(calledFunctionTuple))
if (resolvedFunction && resolvedFunction->isImplemented())
{
case RevertState::Unknown:
wakeUp[calledFunctionTuple].insert(item);
foundUnknown = true;
return;
case RevertState::AllPathsRevert:
return;
case RevertState::HasNonRevertingPath:
break;
CFG::FunctionContractTuple calledFunctionTuple{
findScopeContract(*resolvedFunction, item.contract),
resolvedFunction
};
switch (m_functions.at(calledFunctionTuple))
{
case RevertState::Unknown:
wakeUp[calledFunctionTuple].insert(item);
foundUnknown = true;
return;
case RevertState::AllPathsRevert:
return;
case RevertState::HasNonRevertingPath:
break;
}
}
}
@@ -135,31 +135,29 @@ void ControlFlowRevertPruner::modifyFunctionFlows()
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract);
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
[&](CFGNode* _node, auto&& _addChild) {
for (auto const* functionCall: _node->functionCalls)
if (auto const* functionCall = _node->functionCall)
{
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.first.contract);
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
continue;
if (resolvedFunction && resolvedFunction->isImplemented())
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
{
case RevertState::Unknown:
[[fallthrough]];
case RevertState::AllPathsRevert:
// If the revert states of the functions do not
// change anymore, we treat all "unknown" states as
// "reverting", since they can only be caused by
// recursion.
for (CFGNode * node: _node->exits)
ranges::remove(node->entries, _node);
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
{
case RevertState::Unknown:
[[fallthrough]];
case RevertState::AllPathsRevert:
// If the revert states of the functions do not
// change anymore, we treat all "unknown" states as
// "reverting", since they can only be caused by
// recursion.
for (CFGNode * node: _node->exits)
ranges::remove(node->entries, _node);
_node->exits = {functionFlow.revert};
functionFlow.revert->entries.push_back(_node);
return;
default:
break;
}
_node->exits = {functionFlow.revert};
functionFlow.revert->entries.push_back(_node);
return;
default:
break;
}
}
for (CFGNode* exit: _node->exits)
@@ -437,16 +437,14 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
}
if (
_variable.isConstant() &&
!dynamic_cast<UserDefinedValueType const*>(type) &&
type->containsNestedMapping()
)
m_errorReporter.fatalDeclarationError(
3530_error,
_variable.location(),
"The type contains a (nested) mapping and therefore cannot be a constant."
);
if (_variable.isConstant() && !type->isValueType())
{
bool allowed = false;
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
allowed = arrayType->isByteArray();
if (!allowed)
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
}
_variable.annotation().type = type;
}
+17 -18
View File
@@ -25,6 +25,7 @@
#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
#include <liblangutil/ErrorReporter.h>
#include <boost/algorithm/string.hpp>
@@ -37,7 +38,7 @@ using namespace solidity::frontend;
namespace
{
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, CallableDeclaration const* _declaration = nullptr)
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, FunctionType const* _functionType = nullptr)
{
// Only copy if there is exactly one direct base function.
if (_baseFunctions.size() != 1)
@@ -45,12 +46,6 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
CallableDeclaration const& baseFunction = **_baseFunctions.begin();
auto hasReturnParameter = [](CallableDeclaration const& declaration, size_t _n)
{
return declaration.returnParameterList() &&
declaration.returnParameters().size() > _n;
};
auto& sourceDoc = dynamic_cast<StructurallyDocumentedAnnotation const&>(baseFunction.annotation());
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
@@ -70,21 +65,22 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
DocTag content = it->second;
// Update the parameter name for @return tags
if (_declaration && tag == "return")
if (_functionType && tag == "return")
{
size_t docParaNameEndPos = content.content.find_first_of(" \t");
string const docParameterName = content.content.substr(0, docParaNameEndPos);
if (
hasReturnParameter(*_declaration, n) &&
docParameterName != _declaration->returnParameters().at(n)->name()
_functionType->returnParameterNames().size() > n &&
docParameterName != _functionType->returnParameterNames().at(n)
)
{
bool baseHasNoName =
hasReturnParameter(baseFunction, n) &&
baseFunction.returnParameterList() &&
baseFunction.returnParameters().size() > n &&
baseFunction.returnParameters().at(n)->name().empty();
string paramName = _declaration->returnParameters().at(n)->name();
string paramName = _functionType->returnParameterNames().at(n);
content.content =
(paramName.empty() ? "" : std::move(paramName) + " ") + (
string::npos == docParaNameEndPos || baseHasNoName ?
@@ -127,7 +123,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
{
if (!_function.isConstructor())
handleCallable(_function, _function, _function.annotation());
handleCallable(_function, _function, _function.annotation(), TypeProvider::function(_function));
return true;
}
@@ -136,10 +132,12 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
return false;
auto const* getterType = TypeProvider::function(_variable);
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
copyMissingTags({baseFunction}, _variable.annotation());
copyMissingTags({baseFunction}, _variable.annotation(), getterType);
else if (_variable.annotation().docTags.empty())
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation());
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation(), getterType);
return false;
}
@@ -168,17 +166,18 @@ bool DocStringAnalyser::visit(ErrorDefinition const& _error)
void DocStringAnalyser::handleCallable(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
StructurallyDocumentedAnnotation& _annotation,
FunctionType const* _functionType
)
{
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
copyMissingTags({baseFunction}, _annotation, &_callable);
copyMissingTags({baseFunction}, _annotation, _functionType);
else if (
_annotation.docTags.empty() &&
_callable.annotation().baseFunctions.size() == 1 &&
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
)
copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable);
copyMissingTags(_callable.annotation().baseFunctions, _annotation, _functionType);
}
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(
+2 -1
View File
@@ -54,7 +54,8 @@ private:
void handleCallable(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
StructurallyDocumentedAnnotation& _annotation,
FunctionType const* _functionType = nullptr
);
langutil::ErrorReporter& m_errorReporter;
+16 -16
View File
@@ -73,24 +73,24 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
return {
magicVarDecl("abi", TypeProvider::magic(MagicType::Kind::ABI)),
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)),
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, StateMutability::Pure)),
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, StateMutability::Pure)),
magicVarDecl("block", TypeProvider::magic(MagicType::Kind::Block)),
magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)),
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, StateMutability::View)),
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, StateMutability::Pure)),
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, StateMutability::View)),
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)),
magicVarDecl("msg", TypeProvider::magic(MagicType::Kind::Message)),
magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)),
magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, StateMutability::Pure)),
magicVarDecl("now", TypeProvider::uint256()),
magicVarDecl("require", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
magicVarDecl("require", TypeProvider::function(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
magicVarDecl("revert", TypeProvider::function(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
magicVarDecl("ripemd160", TypeProvider::function(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)),
magicVarDecl("require", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Require, StateMutability::Pure)),
magicVarDecl("require", TypeProvider::function(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, StateMutability::Pure)),
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, StateMutability::Pure)),
magicVarDecl("revert", TypeProvider::function(strings{"string memory"}, strings(), FunctionType::Kind::Revert, StateMutability::Pure)),
magicVarDecl("ripemd160", TypeProvider::function(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, StateMutability::Pure)),
magicVarDecl("selfdestruct", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
magicVarDecl("sha256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
magicVarDecl("sha256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, StateMutability::Pure)),
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)),
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
// Accepts a MagicType that can be any contract type or an Integer type and returns a
@@ -99,8 +99,8 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
strings{},
strings{},
FunctionType::Kind::MetaType,
true,
StateMutability::Pure
StateMutability::Pure,
FunctionType::Options::withArbitraryParameters()
)),
};
}
+16 -9
View File
@@ -29,7 +29,7 @@ void ImmutableValidator::analyze()
{
m_inCreationContext = true;
auto linearizedContracts = m_currentContract.annotation().linearizedBaseContracts | ranges::views::reverse;
auto linearizedContracts = m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse;
for (ContractDefinition const* contract: linearizedContracts)
for (VariableDeclaration const* stateVar: contract->stateVariables())
@@ -62,7 +62,7 @@ void ImmutableValidator::analyze()
visitCallableIfNew(*modDef);
}
checkAllVariablesInitialized(m_currentContract.location());
checkAllVariablesInitialized(m_mostDerivedContract.location());
}
bool ImmutableValidator::visit(Assignment const& _assignment)
@@ -137,7 +137,7 @@ void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifierPath.annotation().referencedDeclaration))
visitCallableIfNew(
*_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ?
callableDef->resolveVirtual(m_currentContract) :
callableDef->resolveVirtual(m_mostDerivedContract) :
*callableDef
);
@@ -147,7 +147,7 @@ void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
void ImmutableValidator::endVisit(Identifier const& _identifier)
{
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_currentContract) : *callableDef);
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_mostDerivedContract) : *callableDef);
if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
analyseVariableReference(*varDecl, _identifier);
}
@@ -160,15 +160,18 @@ void ImmutableValidator::endVisit(Return const& _return)
bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration)
{
FunctionDefinition const* prevConstructor = m_currentConstructor;
m_currentConstructor = nullptr;
ScopedSaveAndRestore constructorGuard{m_currentConstructor, {}};
ScopedSaveAndRestore constructorContractGuard{m_currentConstructorContract, {}};
if (FunctionDefinition const* funcDef = dynamic_cast<decltype(funcDef)>(&_callableDeclaration))
{
ASTNode::listAccept(funcDef->modifiers(), *this);
if (funcDef->isConstructor())
{
m_currentConstructorContract = funcDef->annotation().contract;
m_currentConstructor = funcDef;
}
if (funcDef->isImplemented())
funcDef->body().accept(*this);
@@ -177,8 +180,6 @@ bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDec
if (modDef->isImplemented())
modDef->body().accept(*this);
m_currentConstructor = prevConstructor;
return false;
}
@@ -253,7 +254,8 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location)
{
for (ContractDefinition const* contract: m_currentContract.annotation().linearizedBaseContracts)
for (ContractDefinition const* contract: m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse)
{
for (VariableDeclaration const* varDecl: contract->stateVariables())
if (varDecl->immutable())
if (!util::contains(m_initializedStateVariables, varDecl))
@@ -263,6 +265,11 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
"Construction control flow ends without initializing all immutable state variables."
);
// Don't check further than the current c'tors contract
if (contract == m_currentConstructorContract)
break;
}
}
void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration)
+3 -2
View File
@@ -42,7 +42,7 @@ class ImmutableValidator: private ASTConstVisitor
public:
ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition):
m_currentContract(_contractDefinition),
m_mostDerivedContract(_contractDefinition),
m_errorReporter(_errorReporter)
{ }
@@ -66,7 +66,7 @@ private:
void visitCallableIfNew(Declaration const& _declaration);
ContractDefinition const& m_currentContract;
ContractDefinition const& m_mostDerivedContract;
CallableDeclarationSet m_visitedCallables;
@@ -74,6 +74,7 @@ private:
langutil::ErrorReporter& m_errorReporter;
FunctionDefinition const* m_currentConstructor = nullptr;
ContractDefinition const* m_currentConstructorContract = nullptr;
bool m_inLoop = false;
bool m_inBranch = false;
bool m_inCreationContext = true;
+2 -1
View File
@@ -571,7 +571,8 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
if (!m_scopes[importee])
m_scopes[importee] = make_shared<DeclarationContainer>(nullptr, m_scopes[nullptr].get());
m_scopes[&_import] = m_scopes[importee];
return ASTVisitor::visit(_import);
ASTVisitor::visit(_import);
return false; // Do not recurse into child nodes (Identifier for symbolAliases)
}
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
+22 -13
View File
@@ -530,15 +530,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
}
if (_variable.isConstant())
{
if (!varType->isValueType())
{
bool allowed = false;
if (auto arrayType = dynamic_cast<ArrayType const*>(varType))
allowed = arrayType->isByteArray();
if (!allowed)
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Constants of non-value type not yet implemented.");
}
if (!_variable.value())
m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable.");
else if (!*_variable.value()->annotation().isPure)
@@ -2122,16 +2113,35 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
return;
}
if (functionPointerType->kind() != FunctionType::Kind::External)
if (
functionPointerType->kind() != FunctionType::Kind::External &&
functionPointerType->kind() != FunctionType::Kind::Declaration
)
{
string msg = "Function must be \"public\" or \"external\".";
string msg = "Expected regular external function type, or external view on public function.";
if (functionPointerType->kind() == FunctionType::Kind::Internal)
msg += " Provided internal function.";
else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall)
msg += " Cannot use library functions for abi.encodeCall.";
else if (functionPointerType->kind() == FunctionType::Kind::Creation)
msg += " Provided creation function.";
else
msg += " Cannot use special function.";
SecondarySourceLocation ssl{};
if (functionPointerType->hasDeclaration())
{
ssl.append("Function is declared here:", functionPointerType->declaration().location());
if (functionPointerType->declaration().scope() == m_currentContract)
if (
functionPointerType->declaration().visibility() == Visibility::Public &&
functionPointerType->declaration().scope() == m_currentContract
)
msg += " Did you forget to prefix \"this.\"?";
else if (contains(
m_currentContract->annotation().linearizedBaseContracts,
functionPointerType->declaration().scope()
) && functionPointerType->declaration().scope() != m_currentContract)
msg += " Functions from base contracts have to be external.";
}
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
@@ -2866,7 +2876,6 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
strings(1, ""),
strings(1, ""),
FunctionType::Kind::ObjectCreation,
false,
StateMutability::Pure
);
_newExpression.annotation().isPure = true;