mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
@@ -47,7 +49,7 @@ public:
|
||||
):
|
||||
m_errorReporter(_errorReporter),
|
||||
m_depth(_newDepth),
|
||||
m_types(_types)
|
||||
m_types(std::move(_types))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -591,7 +591,7 @@ bool ControlFlowBuilder::visit(Identifier const& _identifier)
|
||||
if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||
m_currentNode->variableOccurrences.emplace_back(
|
||||
*variableDeclaration,
|
||||
static_cast<Expression const&>(_identifier).annotation().lValueRequested ?
|
||||
static_cast<Expression const&>(_identifier).annotation().willBeWrittenTo ?
|
||||
VariableOccurrence::Kind::Assignment :
|
||||
VariableOccurrence::Kind::Access,
|
||||
_identifier.location()
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::frontend
|
||||
@@ -48,8 +49,8 @@ public:
|
||||
Assignment,
|
||||
InlineAssembly
|
||||
};
|
||||
VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional<langutil::SourceLocation> const& _occurrence = {}):
|
||||
m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(_occurrence)
|
||||
VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional<langutil::SourceLocation> _occurrence = {}):
|
||||
m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(std::move(_occurrence))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/DeclarationTypeChecker.h>
|
||||
|
||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
bool DeclarationTypeChecker::visit(ElementaryTypeName const& _typeName)
|
||||
{
|
||||
if (_typeName.annotation().type)
|
||||
return false;
|
||||
|
||||
_typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName());
|
||||
if (_typeName.stateMutability().has_value())
|
||||
{
|
||||
// for non-address types this was already caught by the parser
|
||||
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
|
||||
switch (*_typeName.stateMutability())
|
||||
{
|
||||
case StateMutability::Payable:
|
||||
_typeName.annotation().type = TypeProvider::payableAddress();
|
||||
break;
|
||||
case StateMutability::NonPayable:
|
||||
_typeName.annotation().type = TypeProvider::address();
|
||||
break;
|
||||
default:
|
||||
typeError(
|
||||
_typeName.location(),
|
||||
"Address types can only be payable or non-payable."
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
|
||||
{
|
||||
if (_struct.annotation().recursive.has_value())
|
||||
{
|
||||
if (!m_currentStructsSeen.empty() && *_struct.annotation().recursive)
|
||||
m_recursiveStructSeen = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_currentStructsSeen.count(&_struct))
|
||||
{
|
||||
_struct.annotation().recursive = true;
|
||||
m_recursiveStructSeen = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool previousRecursiveStructSeen = m_recursiveStructSeen;
|
||||
bool hasRecursiveChild = false;
|
||||
|
||||
m_currentStructsSeen.insert(&_struct);
|
||||
|
||||
for (auto const& member: _struct.members())
|
||||
{
|
||||
m_recursiveStructSeen = false;
|
||||
member->accept(*this);
|
||||
solAssert(member->annotation().type, "");
|
||||
solAssert(member->annotation().type->canBeStored(), "Type cannot be used in struct.");
|
||||
if (m_recursiveStructSeen)
|
||||
hasRecursiveChild = true;
|
||||
}
|
||||
|
||||
if (!_struct.annotation().recursive.has_value())
|
||||
_struct.annotation().recursive = hasRecursiveChild;
|
||||
m_recursiveStructSeen = previousRecursiveStructSeen || *_struct.annotation().recursive;
|
||||
m_currentStructsSeen.erase(&_struct);
|
||||
if (m_currentStructsSeen.empty())
|
||||
m_recursiveStructSeen = false;
|
||||
|
||||
// Check direct recursion, fatal error if detected.
|
||||
auto visitor = [&](StructDefinition const& _struct, auto& _cycleDetector, size_t _depth)
|
||||
{
|
||||
if (_depth >= 256)
|
||||
fatalDeclarationError(_struct.location(), "Struct definition exhausts cyclic dependency validator.");
|
||||
|
||||
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
||||
{
|
||||
Type const* memberType = member->annotation().type;
|
||||
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
||||
{
|
||||
if (arrayType->isDynamicallySized())
|
||||
break;
|
||||
memberType = arrayType->baseType();
|
||||
}
|
||||
if (auto structType = dynamic_cast<StructType const*>(memberType))
|
||||
if (_cycleDetector.run(structType->structDefinition()))
|
||||
return;
|
||||
}
|
||||
};
|
||||
if (util::CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
|
||||
fatalTypeError(_struct.location(), "Recursive struct definition.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName)
|
||||
{
|
||||
if (_typeName.annotation().type)
|
||||
return;
|
||||
|
||||
Declaration const* declaration = _typeName.annotation().referencedDeclaration;
|
||||
solAssert(declaration, "");
|
||||
|
||||
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
|
||||
{
|
||||
if (!m_insideFunctionType && !m_currentStructsSeen.empty())
|
||||
structDef->accept(*this);
|
||||
_typeName.annotation().type = TypeProvider::structType(*structDef, DataLocation::Storage);
|
||||
}
|
||||
else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
|
||||
_typeName.annotation().type = TypeProvider::enumType(*enumDef);
|
||||
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
_typeName.annotation().type = TypeProvider::contract(*contract);
|
||||
else
|
||||
{
|
||||
_typeName.annotation().type = TypeProvider::emptyTuple();
|
||||
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
|
||||
}
|
||||
}
|
||||
bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
|
||||
{
|
||||
if (_typeName.annotation().type)
|
||||
return false;
|
||||
|
||||
bool previousInsideFunctionType = m_insideFunctionType;
|
||||
m_insideFunctionType = true;
|
||||
_typeName.parameterTypeList()->accept(*this);
|
||||
_typeName.returnParameterTypeList()->accept(*this);
|
||||
m_insideFunctionType = previousInsideFunctionType;
|
||||
|
||||
switch (_typeName.visibility())
|
||||
{
|
||||
case Visibility::Internal:
|
||||
case Visibility::External:
|
||||
break;
|
||||
default:
|
||||
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_typeName.isPayable() && _typeName.visibility() != Visibility::External)
|
||||
{
|
||||
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
|
||||
return false;
|
||||
}
|
||||
_typeName.annotation().type = TypeProvider::function(_typeName);
|
||||
return false;
|
||||
}
|
||||
void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
|
||||
{
|
||||
if (_mapping.annotation().type)
|
||||
return;
|
||||
|
||||
if (auto const* typeName = dynamic_cast<UserDefinedTypeName const*>(&_mapping.keyType()))
|
||||
{
|
||||
if (auto const* contractType = dynamic_cast<ContractType const*>(typeName->annotation().type))
|
||||
{
|
||||
if (contractType->contractDefinition().isLibrary())
|
||||
m_errorReporter.fatalTypeError(
|
||||
typeName->location(),
|
||||
"Library types cannot be used as mapping keys."
|
||||
);
|
||||
}
|
||||
else if (typeName->annotation().type->category() != Type::Category::Enum)
|
||||
m_errorReporter.fatalTypeError(
|
||||
typeName->location(),
|
||||
"Only elementary types, contract types or enums are allowed as mapping keys."
|
||||
);
|
||||
}
|
||||
else
|
||||
solAssert(dynamic_cast<ElementaryTypeName const*>(&_mapping.keyType()), "");
|
||||
|
||||
TypePointer keyType = _mapping.keyType().annotation().type;
|
||||
TypePointer valueType = _mapping.valueType().annotation().type;
|
||||
|
||||
// Convert key type to memory.
|
||||
keyType = TypeProvider::withLocationIfReference(DataLocation::Memory, keyType);
|
||||
|
||||
// Convert value type to storage reference.
|
||||
valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType);
|
||||
_mapping.annotation().type = TypeProvider::mapping(keyType, valueType);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
{
|
||||
if (_typeName.annotation().type)
|
||||
return;
|
||||
|
||||
TypePointer baseType = _typeName.baseType().annotation().type;
|
||||
if (!baseType)
|
||||
{
|
||||
solAssert(!m_errorReporter.errors().empty(), "");
|
||||
return;
|
||||
}
|
||||
if (baseType->storageBytes() == 0)
|
||||
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
|
||||
if (Expression const* length = _typeName.length())
|
||||
{
|
||||
TypePointer& lengthTypeGeneric = length->annotation().type;
|
||||
if (!lengthTypeGeneric)
|
||||
lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
|
||||
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric);
|
||||
u256 lengthValue = 0;
|
||||
if (!lengthType || !lengthType->mobileType())
|
||||
typeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
|
||||
else if (lengthType->isZero())
|
||||
typeError(length->location(), "Array with zero length specified.");
|
||||
else if (lengthType->isFractional())
|
||||
typeError(length->location(), "Array with fractional length specified.");
|
||||
else if (lengthType->isNegative())
|
||||
typeError(length->location(), "Array with negative length specified.");
|
||||
else
|
||||
lengthValue = lengthType->literalValue(nullptr);
|
||||
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue);
|
||||
}
|
||||
else
|
||||
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);
|
||||
}
|
||||
void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
{
|
||||
if (_variable.annotation().type)
|
||||
return;
|
||||
|
||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||
if (_variable.immutable() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||
|
||||
if (!_variable.typeName())
|
||||
{
|
||||
// This can still happen in very unusual cases where a developer uses constructs, such as
|
||||
// `var a;`, however, such code will have generated errors already.
|
||||
// However, we cannot blindingly solAssert() for that here, as the TypeChecker (which is
|
||||
// invoking ReferencesResolver) is generating it, so the error is most likely(!) generated
|
||||
// after this step.
|
||||
return;
|
||||
}
|
||||
using Location = VariableDeclaration::Location;
|
||||
Location varLoc = _variable.referenceLocation();
|
||||
DataLocation typeLoc = DataLocation::Memory;
|
||||
|
||||
set<Location> allowedDataLocations = _variable.allowedDataLocations();
|
||||
if (!allowedDataLocations.count(varLoc))
|
||||
{
|
||||
auto locationToString = [](VariableDeclaration::Location _location) -> string
|
||||
{
|
||||
switch (_location)
|
||||
{
|
||||
case Location::Memory: return "\"memory\"";
|
||||
case Location::Storage: return "\"storage\"";
|
||||
case Location::CallData: return "\"calldata\"";
|
||||
case Location::Unspecified: return "none";
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
string errorString;
|
||||
if (!_variable.hasReferenceOrMappingType())
|
||||
errorString = "Data location can only be specified for array, struct or mapping types";
|
||||
else
|
||||
{
|
||||
errorString = "Data location must be " +
|
||||
util::joinHumanReadable(
|
||||
allowedDataLocations | boost::adaptors::transformed(locationToString),
|
||||
", ",
|
||||
" or "
|
||||
);
|
||||
if (_variable.isCallableOrCatchParameter())
|
||||
errorString +=
|
||||
" for " +
|
||||
string(_variable.isReturnParameter() ? "return " : "") +
|
||||
"parameter in" +
|
||||
string(_variable.isExternalCallableParameter() ? " external" : "") +
|
||||
" function";
|
||||
else
|
||||
errorString += " for variable";
|
||||
}
|
||||
errorString += ", but " + locationToString(varLoc) + " was given.";
|
||||
typeError(_variable.location(), errorString);
|
||||
|
||||
solAssert(!allowedDataLocations.empty(), "");
|
||||
varLoc = *allowedDataLocations.begin();
|
||||
}
|
||||
|
||||
// Find correct data location.
|
||||
if (_variable.isEventParameter())
|
||||
{
|
||||
solAssert(varLoc == Location::Unspecified, "");
|
||||
typeLoc = DataLocation::Memory;
|
||||
}
|
||||
else if (_variable.isStateVariable())
|
||||
{
|
||||
solAssert(varLoc == Location::Unspecified, "");
|
||||
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
|
||||
}
|
||||
else if (
|
||||
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
|
||||
dynamic_cast<EnumDefinition const*>(_variable.scope())
|
||||
)
|
||||
// The actual location will later be changed depending on how the type is used.
|
||||
typeLoc = DataLocation::Storage;
|
||||
else
|
||||
switch (varLoc)
|
||||
{
|
||||
case Location::Memory:
|
||||
typeLoc = DataLocation::Memory;
|
||||
break;
|
||||
case Location::Storage:
|
||||
typeLoc = DataLocation::Storage;
|
||||
break;
|
||||
case Location::CallData:
|
||||
typeLoc = DataLocation::CallData;
|
||||
break;
|
||||
case Location::Unspecified:
|
||||
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
|
||||
}
|
||||
|
||||
TypePointer type = _variable.typeName()->annotation().type;
|
||||
if (auto ref = dynamic_cast<ReferenceType const*>(type))
|
||||
{
|
||||
bool isPointer = !_variable.isStateVariable();
|
||||
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
|
||||
}
|
||||
|
||||
_variable.annotation().type = type;
|
||||
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.typeError(_location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalTypeError(_location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
||||
}
|
||||
|
||||
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
||||
{
|
||||
_node.accept(*this);
|
||||
return !m_errorOccurred;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
}
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
/**
|
||||
* Assigns types to declarations.
|
||||
*/
|
||||
class DeclarationTypeChecker: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
DeclarationTypeChecker(
|
||||
langutil::ErrorReporter& _errorReporter,
|
||||
langutil::EVMVersion _evmVersion
|
||||
):
|
||||
m_errorReporter(_errorReporter),
|
||||
m_evmVersion(_evmVersion)
|
||||
{}
|
||||
|
||||
bool check(ASTNode const& _contract);
|
||||
|
||||
private:
|
||||
|
||||
bool visit(ElementaryTypeName const& _typeName) override;
|
||||
void endVisit(UserDefinedTypeName const& _typeName) override;
|
||||
bool visit(FunctionTypeName const& _typeName) override;
|
||||
void endVisit(Mapping const& _mapping) override;
|
||||
void endVisit(ArrayTypeName const& _typeName) override;
|
||||
void endVisit(VariableDeclaration const& _variable) override;
|
||||
bool visit(StructDefinition const& _struct) override;
|
||||
|
||||
/// Adds a new error to the list of errors.
|
||||
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors and throws to abort reference resolving.
|
||||
void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors and throws to abort reference resolving.
|
||||
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
bool m_errorOccurred = false;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
bool m_insideFunctionType = false;
|
||||
bool m_recursiveStructSeen = false;
|
||||
std::set<StructDefinition const*> m_currentStructsSeen;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -160,7 +160,7 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
|
||||
if (!_variableReference.isStateVariable() || !_variableReference.immutable())
|
||||
return;
|
||||
|
||||
if (_expression.annotation().lValueRequested && _expression.annotation().lValueOfOrdinaryAssignment)
|
||||
if (_expression.annotation().willBeWrittenTo && _expression.annotation().lValueOfOrdinaryAssignment)
|
||||
{
|
||||
if (!m_currentConstructor)
|
||||
m_errorReporter.typeError(
|
||||
|
||||
@@ -195,51 +195,6 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> c
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
|
||||
Identifier const& _identifier,
|
||||
vector<Declaration const*> const& _declarations
|
||||
)
|
||||
{
|
||||
solAssert(_declarations.size() > 1, "");
|
||||
vector<Declaration const*> uniqueFunctions;
|
||||
|
||||
for (Declaration const* declaration: _declarations)
|
||||
{
|
||||
solAssert(declaration, "");
|
||||
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
|
||||
solAssert(
|
||||
dynamic_cast<FunctionDefinition const*>(declaration) ||
|
||||
dynamic_cast<EventDefinition const*>(declaration) ||
|
||||
dynamic_cast<VariableDeclaration const*>(declaration) ||
|
||||
dynamic_cast<MagicVariableDeclaration const*>(declaration),
|
||||
"Found overloading involving something not a function, event or a (magic) variable."
|
||||
);
|
||||
|
||||
FunctionTypePointer functionType { declaration->functionType(false) };
|
||||
if (!functionType)
|
||||
functionType = declaration->functionType(true);
|
||||
solAssert(functionType, "Failed to determine the function type of the overloaded.");
|
||||
|
||||
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
|
||||
if (!parameter)
|
||||
m_errorReporter.fatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
|
||||
|
||||
if (uniqueFunctions.end() == find_if(
|
||||
uniqueFunctions.begin(),
|
||||
uniqueFunctions.end(),
|
||||
[&](Declaration const* d)
|
||||
{
|
||||
FunctionType const* newFunctionType = d->functionType(false);
|
||||
if (!newFunctionType)
|
||||
newFunctionType = d->functionType(true);
|
||||
return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType);
|
||||
}
|
||||
))
|
||||
uniqueFunctions.push_back(declaration);
|
||||
}
|
||||
return uniqueFunctions;
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
||||
{
|
||||
for (auto const& instruction: evmasm::c_instructions)
|
||||
|
||||
@@ -95,12 +95,6 @@ public:
|
||||
/// @note Returns a null pointer if any component in the path was not unique or not found.
|
||||
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const;
|
||||
|
||||
/// returns the vector of declarations without repetitions
|
||||
std::vector<Declaration const*> cleanedDeclarations(
|
||||
Identifier const& _identifier,
|
||||
std::vector<Declaration const*> const& _declarations
|
||||
);
|
||||
|
||||
/// Generate and store warnings about variables that are named like instructions.
|
||||
void warnVariablesNamedLikeInstructions();
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
|
||||
#include <libsolidity/analysis/ReferencesResolver.h>
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
@@ -37,7 +35,6 @@
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
@@ -126,40 +123,10 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||
else if (declarations.size() == 1)
|
||||
_identifier.annotation().referencedDeclaration = declarations.front();
|
||||
else
|
||||
_identifier.annotation().overloadedDeclarations =
|
||||
m_resolver.cleanedDeclarations(_identifier, declarations);
|
||||
_identifier.annotation().candidateDeclarations = declarations;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
|
||||
{
|
||||
if (!_typeName.annotation().type)
|
||||
{
|
||||
_typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName());
|
||||
if (_typeName.stateMutability().has_value())
|
||||
{
|
||||
// for non-address types this was already caught by the parser
|
||||
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
|
||||
switch (*_typeName.stateMutability())
|
||||
{
|
||||
case StateMutability::Payable:
|
||||
_typeName.annotation().type = TypeProvider::payableAddress();
|
||||
break;
|
||||
case StateMutability::NonPayable:
|
||||
_typeName.annotation().type = TypeProvider::address();
|
||||
break;
|
||||
default:
|
||||
m_errorReporter.typeError(
|
||||
_typeName.location(),
|
||||
"Address types can only be payable or non-payable."
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
m_returnParameters.push_back(_functionDefinition.returnParameterList().get());
|
||||
@@ -194,113 +161,6 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
|
||||
}
|
||||
|
||||
_typeName.annotation().referencedDeclaration = declaration;
|
||||
|
||||
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
|
||||
_typeName.annotation().type = TypeProvider::structType(*structDef, DataLocation::Storage);
|
||||
else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
|
||||
_typeName.annotation().type = TypeProvider::enumType(*enumDef);
|
||||
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
_typeName.annotation().type = TypeProvider::contract(*contract);
|
||||
else
|
||||
{
|
||||
_typeName.annotation().type = TypeProvider::emptyTuple();
|
||||
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
|
||||
}
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
|
||||
{
|
||||
switch (_typeName.visibility())
|
||||
{
|
||||
case Visibility::Internal:
|
||||
case Visibility::External:
|
||||
break;
|
||||
default:
|
||||
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_typeName.isPayable() && _typeName.visibility() != Visibility::External)
|
||||
{
|
||||
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_typeName.visibility() == Visibility::External)
|
||||
for (auto const& t: _typeName.parameterTypes() + _typeName.returnParameterTypes())
|
||||
{
|
||||
solAssert(t->annotation().type, "Type not set for parameter.");
|
||||
if (!t->annotation().type->interfaceType(false).get())
|
||||
{
|
||||
fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_typeName.annotation().type = TypeProvider::function(_typeName);
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(Mapping const& _mapping)
|
||||
{
|
||||
if (auto const* typeName = dynamic_cast<UserDefinedTypeName const*>(&_mapping.keyType()))
|
||||
{
|
||||
if (auto const* contractType = dynamic_cast<ContractType const*>(typeName->annotation().type))
|
||||
{
|
||||
if (contractType->contractDefinition().isLibrary())
|
||||
m_errorReporter.fatalTypeError(
|
||||
typeName->location(),
|
||||
"Library types cannot be used as mapping keys."
|
||||
);
|
||||
}
|
||||
else if (typeName->annotation().type->category() != Type::Category::Enum)
|
||||
m_errorReporter.fatalTypeError(
|
||||
typeName->location(),
|
||||
"Only elementary types, contract types or enums are allowed as mapping keys."
|
||||
);
|
||||
}
|
||||
else
|
||||
solAssert(dynamic_cast<ElementaryTypeName const*>(&_mapping.keyType()), "");
|
||||
|
||||
TypePointer keyType = _mapping.keyType().annotation().type;
|
||||
TypePointer valueType = _mapping.valueType().annotation().type;
|
||||
|
||||
// Convert key type to memory.
|
||||
keyType = TypeProvider::withLocationIfReference(DataLocation::Memory, keyType);
|
||||
|
||||
// Convert value type to storage reference.
|
||||
valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType);
|
||||
_mapping.annotation().type = TypeProvider::mapping(keyType, valueType);
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
|
||||
{
|
||||
TypePointer baseType = _typeName.baseType().annotation().type;
|
||||
if (!baseType)
|
||||
{
|
||||
solAssert(!m_errorReporter.errors().empty(), "");
|
||||
return;
|
||||
}
|
||||
if (baseType->storageBytes() == 0)
|
||||
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
|
||||
if (Expression const* length = _typeName.length())
|
||||
{
|
||||
TypePointer& lengthTypeGeneric = length->annotation().type;
|
||||
if (!lengthTypeGeneric)
|
||||
lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
|
||||
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric);
|
||||
if (!lengthType || !lengthType->mobileType())
|
||||
fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
|
||||
else if (lengthType->isZero())
|
||||
fatalTypeError(length->location(), "Array with zero length specified.");
|
||||
else if (lengthType->isFractional())
|
||||
fatalTypeError(length->location(), "Array with fractional length specified.");
|
||||
else if (lengthType->isNegative())
|
||||
fatalTypeError(length->location(), "Array with negative length specified.");
|
||||
else
|
||||
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
|
||||
}
|
||||
else
|
||||
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
@@ -321,115 +181,6 @@ bool ReferencesResolver::visit(Return const& _return)
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
|
||||
{
|
||||
if (_variable.annotation().type)
|
||||
return;
|
||||
|
||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||
if (_variable.immutable() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||
|
||||
if (!_variable.typeName())
|
||||
{
|
||||
// This can still happen in very unusual cases where a developer uses constructs, such as
|
||||
// `var a;`, however, such code will have generated errors already.
|
||||
// However, we cannot blindingly solAssert() for that here, as the TypeChecker (which is
|
||||
// invoking ReferencesResolver) is generating it, so the error is most likely(!) generated
|
||||
// after this step.
|
||||
return;
|
||||
}
|
||||
using Location = VariableDeclaration::Location;
|
||||
Location varLoc = _variable.referenceLocation();
|
||||
DataLocation typeLoc = DataLocation::Memory;
|
||||
|
||||
set<Location> allowedDataLocations = _variable.allowedDataLocations();
|
||||
if (!allowedDataLocations.count(varLoc))
|
||||
{
|
||||
auto locationToString = [](VariableDeclaration::Location _location) -> string
|
||||
{
|
||||
switch (_location)
|
||||
{
|
||||
case Location::Memory: return "\"memory\"";
|
||||
case Location::Storage: return "\"storage\"";
|
||||
case Location::CallData: return "\"calldata\"";
|
||||
case Location::Unspecified: return "none";
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
string errorString;
|
||||
if (!_variable.hasReferenceOrMappingType())
|
||||
errorString = "Data location can only be specified for array, struct or mapping types";
|
||||
else
|
||||
{
|
||||
errorString = "Data location must be " +
|
||||
util::joinHumanReadable(
|
||||
allowedDataLocations | boost::adaptors::transformed(locationToString),
|
||||
", ",
|
||||
" or "
|
||||
);
|
||||
if (_variable.isCallableOrCatchParameter())
|
||||
errorString +=
|
||||
" for " +
|
||||
string(_variable.isReturnParameter() ? "return " : "") +
|
||||
"parameter in" +
|
||||
string(_variable.isExternalCallableParameter() ? " external" : "") +
|
||||
" function";
|
||||
else
|
||||
errorString += " for variable";
|
||||
}
|
||||
errorString += ", but " + locationToString(varLoc) + " was given.";
|
||||
typeError(_variable.location(), errorString);
|
||||
|
||||
solAssert(!allowedDataLocations.empty(), "");
|
||||
varLoc = *allowedDataLocations.begin();
|
||||
}
|
||||
|
||||
// Find correct data location.
|
||||
if (_variable.isEventParameter())
|
||||
{
|
||||
solAssert(varLoc == Location::Unspecified, "");
|
||||
typeLoc = DataLocation::Memory;
|
||||
}
|
||||
else if (_variable.isStateVariable())
|
||||
{
|
||||
solAssert(varLoc == Location::Unspecified, "");
|
||||
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
|
||||
}
|
||||
else if (
|
||||
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
|
||||
dynamic_cast<EnumDefinition const*>(_variable.scope())
|
||||
)
|
||||
// The actual location will later be changed depending on how the type is used.
|
||||
typeLoc = DataLocation::Storage;
|
||||
else
|
||||
switch (varLoc)
|
||||
{
|
||||
case Location::Memory:
|
||||
typeLoc = DataLocation::Memory;
|
||||
break;
|
||||
case Location::Storage:
|
||||
typeLoc = DataLocation::Storage;
|
||||
break;
|
||||
case Location::CallData:
|
||||
typeLoc = DataLocation::CallData;
|
||||
break;
|
||||
case Location::Unspecified:
|
||||
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
|
||||
}
|
||||
|
||||
TypePointer type = _variable.typeName()->annotation().type;
|
||||
if (auto ref = dynamic_cast<ReferenceType const*>(type))
|
||||
{
|
||||
bool isPointer = !_variable.isStateVariable();
|
||||
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
|
||||
}
|
||||
|
||||
_variable.annotation().type = type;
|
||||
}
|
||||
|
||||
void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
|
||||
{
|
||||
bool wasInsideFunction = m_yulInsideFunction;
|
||||
@@ -514,18 +265,6 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
visit(*_varDecl.value);
|
||||
}
|
||||
|
||||
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.typeError(_location, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalTypeError(_location, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
|
||||
@@ -76,29 +76,18 @@ private:
|
||||
void endVisit(ForStatement const& _for) override;
|
||||
void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
|
||||
bool visit(Identifier const& _identifier) override;
|
||||
bool visit(ElementaryTypeName const& _typeName) override;
|
||||
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||
void endVisit(FunctionDefinition const& _functionDefinition) override;
|
||||
bool visit(ModifierDefinition const& _modifierDefinition) override;
|
||||
void endVisit(ModifierDefinition const& _modifierDefinition) override;
|
||||
void endVisit(UserDefinedTypeName const& _typeName) override;
|
||||
void endVisit(FunctionTypeName const& _typeName) override;
|
||||
void endVisit(Mapping const& _mapping) override;
|
||||
void endVisit(ArrayTypeName const& _typeName) override;
|
||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||
bool visit(Return const& _return) override;
|
||||
void endVisit(VariableDeclaration const& _variable) override;
|
||||
|
||||
void operator()(yul::FunctionDefinition const& _function) override;
|
||||
void operator()(yul::Identifier const& _identifier) override;
|
||||
void operator()(yul::VariableDeclaration const& _varDecl) override;
|
||||
|
||||
/// Adds a new error to the list of errors.
|
||||
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors and throws to abort reference resolving.
|
||||
void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Adds a new error to the list of errors.
|
||||
void declarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
|
||||
@@ -289,39 +289,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
m_errorReporter.fatalTypeError(_usingFor.libraryName().location(), "Library name expected.");
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(StructDefinition const& _struct)
|
||||
{
|
||||
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
||||
solAssert(type(*member)->canBeStored(), "Type cannot be used in struct.");
|
||||
|
||||
// Check recursion, fatal error if detected.
|
||||
auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector, size_t _depth)
|
||||
{
|
||||
if (_depth >= 256)
|
||||
m_errorReporter.fatalDeclarationError(_struct.location(), "Struct definition exhausting cyclic dependency validator.");
|
||||
|
||||
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
||||
{
|
||||
Type const* memberType = type(*member);
|
||||
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
||||
{
|
||||
if (arrayType->isDynamicallySized())
|
||||
break;
|
||||
memberType = arrayType->baseType();
|
||||
}
|
||||
if (auto structType = dynamic_cast<StructType const*>(memberType))
|
||||
if (_cycleDetector.run(structType->structDefinition()))
|
||||
return;
|
||||
}
|
||||
};
|
||||
if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
|
||||
m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition.");
|
||||
|
||||
ASTNode::listAccept(_struct.members(), *this);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
bool isLibraryFunction = _function.inContractKind() == ContractKind::Library;
|
||||
@@ -520,19 +487,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables.");
|
||||
}
|
||||
|
||||
switch (varType->category())
|
||||
if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
|
||||
{
|
||||
case Type::Category::Array:
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(varType))
|
||||
if (
|
||||
((arrayType->location() == DataLocation::Memory) ||
|
||||
(arrayType->location() == DataLocation::CallData)) &&
|
||||
!arrayType->validForCalldata()
|
||||
)
|
||||
m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
auto result = referenceType->validForLocation(referenceType->location());
|
||||
if (result && _variable.isPublicCallableParameter())
|
||||
result = referenceType->validForLocation(DataLocation::CallData);
|
||||
if (!result)
|
||||
{
|
||||
solAssert(!result.message().empty(), "Expected detailed error message");
|
||||
m_errorReporter.typeError(_variable.location(), result.message());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -633,7 +597,15 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
|
||||
{
|
||||
FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type);
|
||||
if (fun.kind() == FunctionType::Kind::External)
|
||||
{
|
||||
for (auto const& t: _funType.parameterTypes() + _funType.returnParameterTypes())
|
||||
{
|
||||
solAssert(t->annotation().type, "Type not set for parameter.");
|
||||
if (!t->annotation().type->interfaceType(false).get())
|
||||
m_errorReporter.typeError(t->location(), "Internal type cannot be used for external function type.");
|
||||
}
|
||||
solAssert(fun.interfaceType(false), "External function type uses internal types.");
|
||||
}
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
@@ -1305,7 +1277,7 @@ bool TypeChecker::visit(Conditional const& _conditional)
|
||||
_conditional.trueExpression().annotation().isPure &&
|
||||
_conditional.falseExpression().annotation().isPure;
|
||||
|
||||
if (_conditional.annotation().lValueRequested)
|
||||
if (_conditional.annotation().willBeWrittenTo)
|
||||
m_errorReporter.typeError(
|
||||
_conditional.location(),
|
||||
"Conditional expression as left value is not supported yet."
|
||||
@@ -1401,7 +1373,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||
vector<ASTPointer<Expression>> const& components = _tuple.components();
|
||||
TypePointers types;
|
||||
|
||||
if (_tuple.annotation().lValueRequested)
|
||||
if (_tuple.annotation().willBeWrittenTo)
|
||||
{
|
||||
if (_tuple.isInlineArray())
|
||||
m_errorReporter.fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
|
||||
@@ -1432,41 +1404,37 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||
{
|
||||
if (!components[i])
|
||||
m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty.");
|
||||
else if (components[i])
|
||||
{
|
||||
components[i]->accept(*this);
|
||||
types.push_back(type(*components[i]));
|
||||
|
||||
if (types[i]->category() == Type::Category::Tuple)
|
||||
if (dynamic_cast<TupleType const&>(*types[i]).components().empty())
|
||||
{
|
||||
if (_tuple.isInlineArray())
|
||||
m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty.");
|
||||
m_errorReporter.typeError(components[i]->location(), "Tuple component cannot be empty.");
|
||||
}
|
||||
components[i]->accept(*this);
|
||||
types.push_back(type(*components[i]));
|
||||
|
||||
// Note: code generation will visit each of the expression even if they are not assigned from.
|
||||
if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1)
|
||||
if (!dynamic_cast<RationalNumberType const&>(*types[i]).mobileType())
|
||||
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid rational number.");
|
||||
|
||||
if (_tuple.isInlineArray())
|
||||
if (types[i]->category() == Type::Category::Tuple)
|
||||
if (dynamic_cast<TupleType const&>(*types[i]).components().empty())
|
||||
{
|
||||
solAssert(!!types[i], "Inline array cannot have empty components");
|
||||
|
||||
if ((i == 0 || inlineArrayType) && !types[i]->mobileType())
|
||||
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid mobile type.");
|
||||
|
||||
if (i == 0)
|
||||
inlineArrayType = types[i]->mobileType();
|
||||
else if (inlineArrayType)
|
||||
inlineArrayType = Type::commonType(inlineArrayType, types[i]);
|
||||
if (_tuple.isInlineArray())
|
||||
m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty.");
|
||||
m_errorReporter.typeError(components[i]->location(), "Tuple component cannot be empty.");
|
||||
}
|
||||
if (!components[i]->annotation().isPure)
|
||||
isPure = false;
|
||||
|
||||
// Note: code generation will visit each of the expression even if they are not assigned from.
|
||||
if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1)
|
||||
if (!dynamic_cast<RationalNumberType const&>(*types[i]).mobileType())
|
||||
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid rational number.");
|
||||
|
||||
if (_tuple.isInlineArray())
|
||||
{
|
||||
solAssert(!!types[i], "Inline array cannot have empty components");
|
||||
|
||||
if ((i == 0 || inlineArrayType) && !types[i]->mobileType())
|
||||
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid mobile type.");
|
||||
|
||||
if (i == 0)
|
||||
inlineArrayType = types[i]->mobileType();
|
||||
else if (inlineArrayType)
|
||||
inlineArrayType = Type::commonType(inlineArrayType, types[i]);
|
||||
}
|
||||
else
|
||||
types.push_back(TypePointer());
|
||||
if (!components[i]->annotation().isPure)
|
||||
isPure = false;
|
||||
}
|
||||
_tuple.annotation().isPure = isPure;
|
||||
if (_tuple.isInlineArray())
|
||||
@@ -1802,6 +1770,10 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)
|
||||
void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(_function.isConstructor(), "");
|
||||
if (_function.markedVirtual())
|
||||
m_errorReporter.typeError(_function.location(), "Constructors cannot be virtual.");
|
||||
if (_function.overrides())
|
||||
m_errorReporter.typeError(_function.location(), "Constructors cannot override.");
|
||||
if (!_function.returnParameters().empty())
|
||||
m_errorReporter.typeError(_function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
|
||||
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
|
||||
@@ -2782,11 +2754,57 @@ bool TypeChecker::visit(IndexRangeAccess const& _access)
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<Declaration const*> TypeChecker::cleanOverloadedDeclarations(
|
||||
Identifier const& _identifier,
|
||||
vector<Declaration const*> const& _candidates
|
||||
)
|
||||
{
|
||||
solAssert(_candidates.size() > 1, "");
|
||||
vector<Declaration const*> uniqueDeclarations;
|
||||
|
||||
for (Declaration const* declaration: _candidates)
|
||||
{
|
||||
solAssert(declaration, "");
|
||||
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
|
||||
solAssert(
|
||||
dynamic_cast<FunctionDefinition const*>(declaration) ||
|
||||
dynamic_cast<EventDefinition const*>(declaration) ||
|
||||
dynamic_cast<VariableDeclaration const*>(declaration) ||
|
||||
dynamic_cast<MagicVariableDeclaration const*>(declaration),
|
||||
"Found overloading involving something not a function, event or a (magic) variable."
|
||||
);
|
||||
|
||||
FunctionTypePointer functionType {declaration->functionType(false)};
|
||||
if (!functionType)
|
||||
functionType = declaration->functionType(true);
|
||||
solAssert(functionType, "Failed to determine the function type of the overloaded.");
|
||||
|
||||
for (TypePointer parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
|
||||
if (!parameter)
|
||||
m_errorReporter.fatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
|
||||
|
||||
if (uniqueDeclarations.end() == find_if(
|
||||
uniqueDeclarations.begin(),
|
||||
uniqueDeclarations.end(),
|
||||
[&](Declaration const* d)
|
||||
{
|
||||
FunctionType const* newFunctionType = d->functionType(false);
|
||||
if (!newFunctionType)
|
||||
newFunctionType = d->functionType(true);
|
||||
return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType);
|
||||
}
|
||||
))
|
||||
uniqueDeclarations.push_back(declaration);
|
||||
}
|
||||
return uniqueDeclarations;
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(Identifier const& _identifier)
|
||||
{
|
||||
IdentifierAnnotation& annotation = _identifier.annotation();
|
||||
if (!annotation.referencedDeclaration)
|
||||
{
|
||||
annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations);
|
||||
if (!annotation.arguments)
|
||||
{
|
||||
// The identifier should be a public state variable shadowing other functions
|
||||
@@ -3006,7 +3024,7 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
|
||||
|
||||
void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment)
|
||||
{
|
||||
_expression.annotation().lValueRequested = true;
|
||||
_expression.annotation().willBeWrittenTo = true;
|
||||
_expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment;
|
||||
_expression.accept(*this);
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ private:
|
||||
|
||||
void endVisit(InheritanceSpecifier const& _inheritance) override;
|
||||
void endVisit(UsingForDirective const& _usingFor) override;
|
||||
bool visit(StructDefinition const& _struct) override;
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
bool visit(VariableDeclaration const& _variable) override;
|
||||
/// We need to do this manually because we want to pass the bases of the current contract in
|
||||
@@ -154,6 +153,11 @@ private:
|
||||
/// @returns the referenced declaration and throws on error.
|
||||
Declaration const& dereference(UserDefinedTypeName const& _typeName) const;
|
||||
|
||||
std::vector<Declaration const*> cleanOverloadedDeclarations(
|
||||
Identifier const& _reference,
|
||||
std::vector<Declaration const*> const& _candidates
|
||||
);
|
||||
|
||||
/// Runs type checks on @a _expression to infer its type and then checks that it is implicitly
|
||||
/// convertible to @a _expectedType.
|
||||
bool expectType(Expression const& _expression, Type const& _expectedType);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
using namespace std;
|
||||
@@ -41,7 +42,7 @@ public:
|
||||
std::function<void(StateMutability, SourceLocation const&)> _reportMutability
|
||||
):
|
||||
m_dialect(_dialect),
|
||||
m_reportMutability(_reportMutability) {}
|
||||
m_reportMutability(std::move(_reportMutability)) {}
|
||||
|
||||
void operator()(yul::Literal const&) {}
|
||||
void operator()(yul::Identifier const&) {}
|
||||
@@ -196,7 +197,7 @@ void ViewPureChecker::endVisit(Identifier const& _identifier)
|
||||
|
||||
StateMutability mutability = StateMutability::Pure;
|
||||
|
||||
bool writes = _identifier.annotation().lValueRequested;
|
||||
bool writes = _identifier.annotation().willBeWrittenTo;
|
||||
if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
{
|
||||
if (varDecl->isStateVariable() && !varDecl->isConstant())
|
||||
@@ -331,7 +332,7 @@ bool ViewPureChecker::visit(MemberAccess const& _memberAccess)
|
||||
void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
StateMutability mutability = StateMutability::Pure;
|
||||
bool writes = _memberAccess.annotation().lValueRequested;
|
||||
bool writes = _memberAccess.annotation().willBeWrittenTo;
|
||||
|
||||
ASTString const& member = _memberAccess.memberName();
|
||||
switch (_memberAccess.expression().annotation().type->category())
|
||||
@@ -401,7 +402,7 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess)
|
||||
solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, "");
|
||||
else
|
||||
{
|
||||
bool writes = _indexAccess.annotation().lValueRequested;
|
||||
bool writes = _indexAccess.annotation().willBeWrittenTo;
|
||||
if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage))
|
||||
reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location());
|
||||
}
|
||||
@@ -409,7 +410,7 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess)
|
||||
|
||||
void ViewPureChecker::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||
{
|
||||
bool writes = _indexRangeAccess.annotation().lValueRequested;
|
||||
bool writes = _indexRangeAccess.annotation().willBeWrittenTo;
|
||||
if (_indexRangeAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage))
|
||||
reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexRangeAccess.location());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user