aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis
diff options
context:
space:
mode:
authorAnurag Dashputre <anurag4u80@gmail.com>2018-08-23 14:26:45 +0800
committerGitHub <noreply@github.com>2018-08-23 14:26:45 +0800
commit8497dcd721ff0a113374c0c1e1778d44265398a6 (patch)
tree2833ab7b3c7513647c0476d0e5d33dc11fcd6951 /libsolidity/analysis
parent55524788e2829b3a2b9c6c513f78ba2074aa3385 (diff)
parent410d288dfc2e08c42df58c7e01ad5c332ce92727 (diff)
downloaddexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar
dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.gz
dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.bz2
dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.lz
dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.xz
dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.zst
dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.zip
Merge branch 'develop' into anurag_issue_3667
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.cpp5
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp7
-rw-r--r--libsolidity/analysis/DocStringAnalyser.cpp34
-rw-r--r--libsolidity/analysis/DocStringAnalyser.h11
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp11
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h2
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp192
-rw-r--r--libsolidity/analysis/TypeChecker.cpp164
-rw-r--r--libsolidity/analysis/TypeChecker.h8
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp2
10 files changed, 283 insertions, 153 deletions
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp
index 483d08c8..ab6569be 100644
--- a/libsolidity/analysis/ControlFlowAnalyzer.cpp
+++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp
@@ -75,7 +75,10 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
{
auto& unassignedAtFunctionEntry = unassigned[_functionEntry];
for (auto const& returnParameter: _function.returnParameterList()->parameters())
- if (returnParameter->type()->dataStoredIn(DataLocation::Storage))
+ if (
+ returnParameter->type()->dataStoredIn(DataLocation::Storage) ||
+ returnParameter->type()->category() == Type::Category::Mapping
+ )
unassignedAtFunctionEntry.insert(returnParameter.get());
}
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index 347daaf8..5f980788 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -138,19 +138,22 @@ vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _n
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
{
static size_t const MAXIMUM_EDIT_DISTANCE = 2;
+ // because the function below has quadratic runtime - it will not magically improve once a better algorithm is discovered ;)
+ // since 80 is the suggested line length limit, we use 80^2 as length threshold
+ static size_t const MAXIMUM_LENGTH_THRESHOLD = 80 * 80;
vector<ASTString> similar;
for (auto const& declaration: m_declarations)
{
string const& declarationName = declaration.first;
- if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
+ if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD))
similar.push_back(declarationName);
}
for (auto const& declaration: m_invisibleDeclarations)
{
string const& declarationName = declaration.first;
- if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
+ if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD))
similar.push_back(declarationName);
}
diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp
index b3fb5258..c1b97def 100644
--- a/libsolidity/analysis/DocStringAnalyser.cpp
+++ b/libsolidity/analysis/DocStringAnalyser.cpp
@@ -48,7 +48,10 @@ bool DocStringAnalyser::visit(ContractDefinition const& _contract)
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
{
- handleCallable(_function, _function, _function.annotation());
+ if (_function.isConstructor())
+ handleConstructor(_function, _function, _function.annotation());
+ else
+ handleCallable(_function, _function, _function.annotation());
return true;
}
@@ -66,15 +69,11 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
return true;
}
-void DocStringAnalyser::handleCallable(
+void DocStringAnalyser::checkParameters(
CallableDeclaration const& _callable,
- Documented const& _node,
DocumentedAnnotation& _annotation
)
{
- static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"};
- parseDocStrings(_node, _annotation, validTags, "functions");
-
set<string> validParams;
for (auto const& p: _callable.parameters())
validParams.insert(p->name());
@@ -89,6 +88,29 @@ void DocStringAnalyser::handleCallable(
i->second.paramName +
"\" not found in the parameter list of the function."
);
+
+}
+
+void DocStringAnalyser::handleConstructor(
+ CallableDeclaration const& _callable,
+ Documented const& _node,
+ DocumentedAnnotation& _annotation
+)
+{
+ static const set<string> validTags = set<string>{"author", "dev", "notice", "param"};
+ parseDocStrings(_node, _annotation, validTags, "constructor");
+ checkParameters(_callable, _annotation);
+}
+
+void DocStringAnalyser::handleCallable(
+ CallableDeclaration const& _callable,
+ Documented const& _node,
+ DocumentedAnnotation& _annotation
+)
+{
+ static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"};
+ parseDocStrings(_node, _annotation, validTags, "functions");
+ checkParameters(_callable, _annotation);
}
void DocStringAnalyser::parseDocStrings(
diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h
index 158b4060..5d339428 100644
--- a/libsolidity/analysis/DocStringAnalyser.h
+++ b/libsolidity/analysis/DocStringAnalyser.h
@@ -48,6 +48,17 @@ private:
virtual bool visit(ModifierDefinition const& _modifier) override;
virtual bool visit(EventDefinition const& _event) override;
+ void checkParameters(
+ CallableDeclaration const& _callable,
+ DocumentedAnnotation& _annotation
+ );
+
+ void handleConstructor(
+ CallableDeclaration const& _callable,
+ Documented const& _node,
+ DocumentedAnnotation& _annotation
+ );
+
void handleCallable(
CallableDeclaration const& _callable,
Documented const& _node,
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index c5c429ce..b452a49a 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -626,6 +626,17 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
closeCurrentScope();
}
+bool DeclarationRegistrationHelper::visit(FunctionTypeName& _funTypeName)
+{
+ enterNewSubScope(_funTypeName);
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(FunctionTypeName&)
+{
+ closeCurrentScope();
+}
+
bool DeclarationRegistrationHelper::visit(Block& _block)
{
_block.setScope(m_currentScope);
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 8178ee17..a72c21e3 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -171,6 +171,8 @@ private:
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
+ bool visit(FunctionTypeName& _funTypeName) override;
+ void endVisit(FunctionTypeName& _funTypeName) override;
bool visit(Block& _block) override;
void endVisit(Block& _block) override;
bool visit(ForStatement& _forLoop) override;
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index fa0888dd..f33de7b7 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -30,7 +30,10 @@
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <libdevcore/StringUtils.h>
+
#include <boost/algorithm/string.hpp>
+#include <boost/range/adaptor/transformed.hpp>
using namespace std;
using namespace dev;
@@ -155,7 +158,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
_typeName.annotation().type = make_shared<ContractType>(*contract);
else
+ {
+ _typeName.annotation().type = make_shared<TupleType>();
typeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
+ }
}
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
@@ -166,13 +172,13 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
case VariableDeclaration::Visibility::External:
break;
default:
- typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
+ fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
return;
}
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
{
- typeError(_typeName.location(), "Only external function types can be payable.");
+ fatalTypeError(_typeName.location(), "Only external function types can be payable.");
return;
}
@@ -182,7 +188,7 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
solAssert(t->annotation().type, "Type not set for parameter.");
if (!t->annotation().type->canBeUsedExternally(false))
{
- typeError(t->location(), "Internal type cannot be used for external function type.");
+ fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
return;
}
}
@@ -300,6 +306,9 @@ 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.typeName())
{
// This can still happen in very unusual cases where a developer uses constructs, such as
@@ -309,127 +318,92 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
// after this step.
return;
}
-
- TypePointer type;
- type = _variable.typeName()->annotation().type;
using Location = VariableDeclaration::Location;
Location varLoc = _variable.referenceLocation();
DataLocation typeLoc = DataLocation::Memory;
- // References are forced to calldata for external function parameters (not return)
- // and memory for parameters (also return) of publicly visible functions.
- // They default to memory for function parameters and storage for local variables.
- // As an exception, "storage" is allowed for library functions.
- if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
+
+ set<Location> allowedDataLocations = _variable.allowedDataLocations();
+ if (!allowedDataLocations.count(varLoc))
{
- bool isPointer = true;
- if (_variable.isExternalCallableParameter())
+ auto locationToString = [](VariableDeclaration::Location _location) -> string
{
- auto const& contract = dynamic_cast<ContractDefinition const&>(
- *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
- );
- if (contract.isLibrary())
- {
- if (varLoc == Location::Memory)
- fatalTypeError(_variable.location(),
- "Location has to be calldata or storage for external "
- "library functions (remove the \"memory\" keyword)."
- );
- }
- else
+ switch (_location)
{
- // force location of external function parameters (not return) to calldata
- if (varLoc != Location::CallData && varLoc != Location::Default)
- fatalTypeError(_variable.location(),
- "Location has to be calldata for external functions "
- "(remove the \"memory\" or \"storage\" keyword)."
- );
+ case Location::Memory: return "\"memory\"";
+ case Location::Storage: return "\"storage\"";
+ case Location::CallData: return "\"calldata\"";
+ case Location::Default: return "none";
}
- if (varLoc == Location::Default || varLoc == Location::CallData)
- typeLoc = DataLocation::CallData;
- else
- typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
- }
- else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic())
+ return {};
+ };
+
+ string errorString;
+ if (!_variable.hasReferenceOrMappingType())
+ errorString = "Data location can only be specified for array, struct or mapping types";
+ else
{
- auto const& contract = dynamic_cast<ContractDefinition const&>(
- *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ errorString = "Data location must be " +
+ joinHumanReadable(
+ allowedDataLocations | boost::adaptors::transformed(locationToString),
+ ", ",
+ " or "
);
- // force locations of public or external function (return) parameters to memory
- if (varLoc != Location::Memory && varLoc != Location::Default && !contract.isLibrary())
- fatalTypeError(_variable.location(),
- "Location has to be memory for publicly visible functions "
- "(remove the \"storage\" or \"calldata\" keyword)."
- );
- if (varLoc == Location::Default || !contract.isLibrary())
- typeLoc = DataLocation::Memory;
+ if (_variable.isCallableParameter())
+ errorString +=
+ " for " +
+ string(_variable.isReturnParameter() ? "return " : "") +
+ "parameter in" +
+ string(_variable.isExternalCallableParameter() ? " external" : "") +
+ " function";
else
- {
- if (varLoc == Location::CallData)
- fatalTypeError(_variable.location(),
- "Location cannot be calldata for non-external functions "
- "(remove the \"calldata\" keyword)."
- );
- typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
- }
+ errorString += " for variable";
}
- else
+ 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::Default, "");
+ typeLoc = DataLocation::Memory;
+ }
+ else if (_variable.isStateVariable())
+ {
+ solAssert(varLoc == Location::Default, "");
+ typeLoc = _variable.isConstant() ? 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)
{
- if (_variable.isConstant())
- {
- if (varLoc != Location::Default && varLoc != Location::Memory)
- fatalTypeError(
- _variable.location(),
- "Data location has to be \"memory\" (or unspecified) for constants."
- );
- typeLoc = DataLocation::Memory;
- }
- else if (varLoc == Location::Default)
- {
- if (_variable.isCallableParameter())
- typeLoc = DataLocation::Memory;
- else
- {
- typeLoc = DataLocation::Storage;
- if (_variable.isLocalVariable())
- typeError(
- _variable.location(),
- "Data location must be specified as either \"memory\" or \"storage\"."
- );
- }
- }
- else
- {
- switch (varLoc)
- {
- case Location::Memory:
- typeLoc = DataLocation::Memory;
- break;
- case Location::Storage:
- typeLoc = DataLocation::Storage;
- break;
- case Location::CallData:
- fatalTypeError(_variable.location(),
- "Variable cannot be declared as \"calldata\" (remove the \"calldata\" keyword)."
- );
- break;
- default:
- solAssert(false, "Unknown data location");
- }
- }
- isPointer = !_variable.isStateVariable();
+ case Location::Memory:
+ typeLoc = DataLocation::Memory;
+ break;
+ case Location::Storage:
+ typeLoc = DataLocation::Storage;
+ break;
+ case Location::CallData:
+ typeLoc = DataLocation::CallData;
+ break;
+ case Location::Default:
+ solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
- type = ref->copyForLocation(typeLoc, isPointer);
- }
- else if (dynamic_cast<MappingType const*>(type.get()))
+
+ TypePointer type = _variable.typeName()->annotation().type;
+ if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{
- if (_variable.isLocalVariable() && varLoc != Location::Storage)
- typeError(
- _variable.location(),
- "Data location for mappings must be specified as \"storage\"."
- );
+ bool isPointer = !_variable.isStateVariable();
+ type = ref->copyForLocation(typeLoc, isPointer);
}
- else if (varLoc != Location::Default && !ref)
- typeError(_variable.location(), "Data location can only be given for array or struct types.");
_variable.annotation().type = type;
}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index bcc3757a..fca64f6a 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -525,6 +525,75 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
);
}
+TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
+{
+ vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
+ if (arguments.size() != 2)
+ m_errorReporter.typeError(
+ _functionCall.location(),
+ "This function takes two arguments, but " +
+ toString(arguments.size()) +
+ " were provided."
+ );
+ if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
+ m_errorReporter.typeError(
+ arguments.front()->location(),
+ "Invalid type for argument in function call. "
+ "Invalid implicit conversion from " +
+ type(*arguments.front())->toString() +
+ " to bytes memory requested."
+ );
+
+ TypePointer returnType = make_shared<TupleType>();
+
+ if (arguments.size() < 2)
+ return returnType;
+
+ // The following is a rather syntactic restriction, but we check it here anyway:
+ // The second argument has to be a tuple expression containing type names.
+ TupleExpression const* tupleExpression = dynamic_cast<TupleExpression const*>(arguments[1].get());
+ if (!tupleExpression)
+ {
+ m_errorReporter.typeError(
+ arguments[1]->location(),
+ "The second argument to \"abi.decode\" has to be a tuple of types."
+ );
+ return returnType;
+ }
+
+ vector<TypePointer> components;
+ for (auto const& typeArgument: tupleExpression->components())
+ {
+ solAssert(typeArgument, "");
+ if (TypeType const* argTypeType = dynamic_cast<TypeType const*>(type(*typeArgument).get()))
+ {
+ TypePointer actualType = argTypeType->actualType();
+ solAssert(actualType, "");
+ // We force memory because the parser currently cannot handle
+ // data locations. Furthermore, storage can be a little dangerous and
+ // calldata is not really implemented anyway.
+ actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType);
+ solAssert(
+ !actualType->dataStoredIn(DataLocation::CallData) &&
+ !actualType->dataStoredIn(DataLocation::Storage),
+ ""
+ );
+ if (!actualType->fullEncodingType(false, _abiEncoderV2, false))
+ m_errorReporter.typeError(
+ typeArgument->location(),
+ "Decoding type " + actualType->toString(false) + " not supported."
+ );
+ components.push_back(actualType);
+ }
+ else
+ {
+ m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name.");
+ components.push_back(make_shared<TupleType>());
+ }
+ }
+ return make_shared<TupleType>(components);
+}
+
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
{
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
@@ -580,9 +649,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
bool TypeChecker::visit(StructDefinition const& _struct)
{
- if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
- m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces.");
-
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
if (!type(*member)->canBeStored())
m_errorReporter.typeError(member->location(), "Type cannot be used in struct.");
@@ -610,7 +676,10 @@ bool TypeChecker::visit(StructDefinition const& _struct)
if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition.");
+ bool insideStruct = true;
+ swap(insideStruct, m_insideStruct);
ASTNode::listAccept(_struct.members(), *this);
+ m_insideStruct = insideStruct;
return false;
}
@@ -629,7 +698,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
- if (!type(*var)->canLiveOutsideStorage())
+ if (
+ type(*var)->category() == Type::Category::Mapping &&
+ !type(*var)->dataStoredIn(DataLocation::Storage)
+ )
+ m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\".");
+ else if (
+ !type(*var)->canLiveOutsideStorage() &&
+ _function.visibility() > FunctionDefinition::Visibility::Internal
+ )
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
@@ -690,10 +767,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(VariableDeclaration const& _variable)
{
// Forbid any variable declarations inside interfaces unless they are part of
- // a function's input/output parameters.
+ // * a function's input/output parameters,
+ // * or inside of a struct definition.
if (
m_scope->contractKind() == ContractDefinition::ContractKind::Interface
&& !_variable.isCallableParameter()
+ && !m_insideStruct
)
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
@@ -711,8 +790,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
expectType(*_variable.value(), *varType);
if (_variable.isConstant())
{
- if (!_variable.isStateVariable())
- m_errorReporter.typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
if (!_variable.type()->isValueType())
{
bool allowed = false;
@@ -742,7 +819,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
)
m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables.");
- if (varType->category() == Type::Category::Array)
+ switch (varType->category())
+ {
+ case Type::Category::Array:
if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
if (
((arrayType->location() == DataLocation::Memory) ||
@@ -750,6 +829,18 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
!arrayType->validForCalldata()
)
m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded.");
+ break;
+ case Type::Category::Mapping:
+ if (auto mappingType = dynamic_cast<MappingType const*>(varType.get()))
+ if (
+ mappingType->keyType()->isDynamicallySized() &&
+ _variable.visibility() == Declaration::Visibility::Public
+ )
+ m_errorReporter.typeError(_variable.location(), "Dynamically-sized keys for public mappings are not supported.");
+ break;
+ default:
+ break;
+ }
return false;
}
@@ -818,7 +909,17 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
{
if (var->isIndexed())
+ {
numIndexed++;
+ if (
+ _eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
+ dynamic_cast<ReferenceType const*>(type(*var).get())
+ )
+ m_errorReporter.typeError(
+ var->location(),
+ "Indexed reference types cannot yet be used with ABIEncoderV2."
+ );
+ }
if (!type(*var)->canLiveOutsideStorage())
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (!type(*var)->interfaceType(false))
@@ -1282,7 +1383,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
if (
kind == FunctionType::Kind::BareCall ||
kind == FunctionType::Kind::BareCallCode ||
- kind == FunctionType::Kind::BareDelegateCall
+ kind == FunctionType::Kind::BareDelegateCall ||
+ kind == FunctionType::Kind::BareStaticCall
)
m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used.");
else if (kind == FunctionType::Kind::Send)
@@ -1626,10 +1728,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else
{
TypePointer const& argType = type(*arguments.front());
+ // Resulting data location is memory unless we are converting from a reference
+ // type with a different data location.
+ // (data location cannot yet be specified for type conversions)
+ DataLocation dataLoc = DataLocation::Memory;
if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get()))
- // do not change the data location when converting
- // (data location cannot yet be specified for type conversions)
- resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
+ dataLoc = argRefType->location();
+ resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType);
if (!argType->isExplicitlyConvertibleTo(*resultType))
m_errorReporter.typeError(
_functionCall.location(),
@@ -1674,6 +1779,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
return false;
}
+ if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall())
+ m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version.");
+
auto returnTypes =
allowDynamicTypes ?
functionType->returnParameterTypes() :
@@ -1716,7 +1824,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
- if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
+ bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
+
+ if (functionType->kind() == FunctionType::Kind::ABIDecode)
+ _functionCall.annotation().type = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
+ else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
{
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
m_errorReporter.typeError(
@@ -1750,7 +1862,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else if (
functionType->kind() == FunctionType::Kind::BareCall ||
functionType->kind() == FunctionType::Kind::BareCallCode ||
- functionType->kind() == FunctionType::Kind::BareDelegateCall
+ functionType->kind() == FunctionType::Kind::BareDelegateCall ||
+ functionType->kind() == FunctionType::Kind::BareStaticCall
)
{
if (arguments.empty())
@@ -1771,8 +1884,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
else if (isPositionalCall)
{
- bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
-
for (size_t i = 0; i < arguments.size(); ++i)
{
auto const& argType = type(*arguments[i]);
@@ -1785,7 +1896,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
errored = true;
}
- if (!errored && !argType->fullEncodingType(false, abiEncodeV2, !functionType->padArguments()))
+ if (!errored && !argType->fullEncodingType(false, abiEncoderV2, !functionType->padArguments()))
m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
@@ -1800,7 +1911,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (
functionType->kind() == FunctionType::Kind::BareCall ||
functionType->kind() == FunctionType::Kind::BareCallCode ||
- functionType->kind() == FunctionType::Kind::BareDelegateCall
+ functionType->kind() == FunctionType::Kind::BareDelegateCall ||
+ functionType->kind() == FunctionType::Kind::BareStaticCall
)
msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
else if (
@@ -2348,22 +2460,6 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte
"."
);
}
-
- if (
- type(_expression)->category() == Type::Category::RationalNumber &&
- _expectedType.category() == Type::Category::FixedBytes
- )
- {
- auto literal = dynamic_cast<Literal const*>(&_expression);
-
- if (literal && !literal->isHexNumber())
- m_errorReporter.warning(
- _expression.location(),
- "Decimal literal assigned to bytesXX variable will be left-aligned. "
- "Use an explicit conversion to silence this warning."
- );
- }
-
}
void TypeChecker::requireLValue(Expression const& _expression)
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index b696de85..4be0d1e4 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -91,6 +91,11 @@ private:
// and reports an error, if not.
void checkExpressionAssignment(Type const& _type, Expression const& _expression);
+ /// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the
+ /// return type (which is basically the second argument) if successful. It returns
+ /// the empty tuple type or error.
+ TypePointer typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2);
+
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
virtual void endVisit(UsingForDirective const& _usingFor) override;
virtual bool visit(StructDefinition const& _struct) override;
@@ -149,6 +154,9 @@ private:
/// Flag indicating whether we are currently inside an EmitStatement.
bool m_insideEmitStatement = false;
+ /// Flag indicating whether we are currently inside a StructDefinition.
+ bool m_insideStruct = false;
+
ErrorReporter& m_errorReporter;
};
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index d936ada0..e92ad2fa 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -295,7 +295,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{
// we can ignore the kind of magic and only look at the name of the member
set<string> static const pureMembers{
- "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash"
+ "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode", "data", "sig", "blockhash"
};
if (!pureMembers.count(member))
mutability = StateMutability::View;