aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r--libsolidity/analysis/TypeChecker.cpp79
-rw-r--r--libsolidity/analysis/TypeChecker.h5
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp2
3 files changed, 81 insertions, 5 deletions
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 8b609a31..43e894e5 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()));
@@ -1727,7 +1796,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(
@@ -1782,8 +1855,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]);
@@ -1796,7 +1867,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]))
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index ed316f9c..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;
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;