aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2015-10-10 01:35:41 +0800
committerchriseth <c@ethdev.com>2015-10-10 01:35:41 +0800
commita5d12b876180088904a026c472da23201a35d59c (patch)
tree277c5e2da4bc1ea04f10b3369f96c5f087fb01db /libsolidity
parentc54a033bf036d16651fb992689adfcffb2f3a951 (diff)
downloaddexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar
dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.gz
dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.bz2
dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.lz
dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.xz
dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.zst
dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.zip
Introduced tuple type and added multi variable declarations to type
checker.
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/AST.h4
-rw-r--r--libsolidity/Parser.cpp24
-rw-r--r--libsolidity/TypeChecker.cpp144
-rw-r--r--libsolidity/Types.cpp55
-rw-r--r--libsolidity/Types.h44
5 files changed, 184 insertions, 87 deletions
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index 354beb7c..5c4c889d 100644
--- a/libsolidity/AST.h
+++ b/libsolidity/AST.h
@@ -558,7 +558,9 @@ protected:
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
- ASTPointer<Expression> m_value; ///< the assigned value, can be missing
+ /// Initially assigned value, can be missing. For local variables, this is stored inside
+ /// VariableDeclarationStatement and not here.
+ ASTPointer<Expression> m_value;
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant.
diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp
index c6b74e1c..e02a70d5 100644
--- a/libsolidity/Parser.cpp
+++ b/libsolidity/Parser.cpp
@@ -783,14 +783,34 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
)
{
// Parse `var (a, b, ,, c) = ...` into a single VariableDeclarationStatement with multiple variables.
- solAssert(false, "To be implemented.");
+ m_scanner->next();
+ m_scanner->next();
+ do
+ {
+ ASTPointer<VariableDeclaration> var;
+ if (m_scanner->currentToken() == Token::Comma)
+ m_scanner->next();
+ else
+ {
+ ASTNodeFactory varDeclNodeFactory(*this);
+ ASTPointer<ASTString> name = expectIdentifierToken();
+ var = varDeclNodeFactory.createNode<VariableDeclaration>(
+ ASTPointer<TypeName>(),
+ name,
+ ASTPointer<Expression>(),
+ VariableDeclaration::Visibility::Default
+ );
+ }
+ variables.push_back(var);
+ } while (m_scanner->currentToken() != Token::RParen);
+ nodeFactory.markEndPosition();
+ m_scanner->next();
}
else
{
VarDeclParserOptions options;
options.allowVar = true;
options.allowLocationSpecifier = true;
- options.allowInitialValue = false;
variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType));
}
if (m_scanner->currentToken() == Token::Assign)
diff --git a/libsolidity/TypeChecker.cpp b/libsolidity/TypeChecker.cpp
index 8207e1ff..b1007276 100644
--- a/libsolidity/TypeChecker.cpp
+++ b/libsolidity/TypeChecker.cpp
@@ -449,18 +449,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
}
if (_variable.value())
expectType(*_variable.value(), *varType);
- else
- {
- if (auto ref = dynamic_cast<ReferenceType const *>(varType.get()))
- if (ref->dataStoredIn(DataLocation::Storage) && _variable.isLocalVariable() && !_variable.isCallableParameter())
- {
- auto err = make_shared<Warning>();
- *err <<
- errinfo_sourceLocation(_variable.location()) <<
- errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + _variable.name() + "'?");
- m_errors.push_back(err);
- }
- }
if (!_variable.isStateVariable())
{
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
@@ -601,36 +589,101 @@ void TypeChecker::endVisit(Return const& _return)
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
- solAssert(_statement.declarations().size() == 1, "To be implemented.");
- solAssert(!!_statement.declarations().front(), "");
- VariableDeclaration const& var = *_statement.declarations().front();
- solAssert(!var.value(), "Value has to be tied to statement.");
- if (!var.annotation().type)
+ solAssert(!_statement.declarations().empty(), "");
+ if (!_statement.initialValue())
{
- solAssert(!var.typeName(), "");
- // Infer type from value.
- if (!_statement.initialValue())
+ // No initial value is only permitted for single variables with specified type.
+ if (_statement.declarations().size() != 1 || !_statement.declarations().front())
+ fatalTypeError(_statement, "Assignment necessary for type detection.");
+ VariableDeclaration const& varDecl = *_statement.declarations().front();
+ if (!varDecl.annotation().type)
fatalTypeError(_statement, "Assignment necessary for type detection.");
- _statement.initialValue()->accept(*this);
-
- TypePointer const& valueType = type(*_statement.initialValue());
- solAssert(!!valueType, "");
- if (
- valueType->category() == Type::Category::IntegerConstant &&
- !dynamic_pointer_cast<IntegerConstantType const>(valueType)->integerType()
- )
- fatalTypeError(*_statement.initialValue(), "Invalid integer constant " + valueType->toString() + ".");
- else if (valueType->category() == Type::Category::Void)
- fatalTypeError(_statement, "Variable cannot have void type.");
- var.annotation().type = valueType->mobileType();
- var.accept(*this);
+ if (auto ref = dynamic_cast<ReferenceType const*>(varDecl.annotation().type.get()))
+ {
+ if (ref->dataStoredIn(DataLocation::Storage))
+ {
+ auto err = make_shared<Warning>();
+ *err <<
+ errinfo_sourceLocation(varDecl.location()) <<
+ errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?");
+ m_errors.push_back(err);
+ }
+ }
+ varDecl.accept(*this);
return false;
}
- else
+
+ // Here we have an initial value and might have to derive some types before we can visit
+ // the variable declaration(s).
+
+ _statement.initialValue()->accept(*this);
+ shared_ptr<TupleType const> valueType = dynamic_pointer_cast<TupleType const>(_statement.initialValue()->annotation().type);
+ if (!valueType)
+ valueType = make_shared<TupleType const>(TypePointers{_statement.initialValue()->annotation().type});
+
+ vector<ASTPointer<VariableDeclaration>> variables = _statement.declarations();
+ // If numbers do not match, fill up if variables begin or end empty (not both).
+ if (valueType->components().size() != variables.size())
+ {
+ if (!variables.front() && !variables.back())
+ fatalTypeError(
+ _statement,
+ "Wildcard both at beginning and end of variable declaration list is only allowed "
+ "if the number of components is equal."
+ );
+ while (valueType->components().size() > variables.size())
+ if (!variables.front())
+ variables.insert(variables.begin(), shared_ptr<VariableDeclaration>());
+ else
+ variables.push_back(shared_ptr<VariableDeclaration>());
+ while (valueType->components().size() < variables.size())
+ if (!variables.empty() && !variables.front())
+ variables.erase(variables.begin());
+ else if (!variables.empty() && !variables.back())
+ variables.pop_back();
+ else
+ break;
+ if (valueType->components().size() != variables.size())
+ fatalTypeError(
+ _statement,
+ "Unable to match the number of variables to the number of values."
+ );
+ }
+ solAssert(variables.size() == valueType->components().size(), "");
+
+ for (size_t i = 0; i < variables.size(); ++i)
{
- var.accept(*this);
- if (_statement.initialValue())
- expectType(*_statement.initialValue(), *var.annotation().type);
+ if (!variables[i])
+ continue;
+ VariableDeclaration const& var = *variables[i];
+ solAssert(!var.value(), "Value has to be tied to statement.");
+ TypePointer const& valueComponentType = valueType->components()[i];
+ solAssert(!!valueComponentType, "");
+ if (!var.annotation().type)
+ {
+ // Infer type from value.
+ solAssert(!var.typeName(), "");
+ if (
+ valueComponentType->category() == Type::Category::IntegerConstant &&
+ !dynamic_pointer_cast<IntegerConstantType const>(valueComponentType)->integerType()
+ )
+ fatalTypeError(*_statement.initialValue(), "Invalid integer constant " + valueComponentType->toString() + ".");
+ var.annotation().type = valueComponentType->mobileType();
+ var.accept(*this);
+ }
+ else
+ {
+ var.accept(*this);
+ if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
+ typeError(
+ _statement,
+ "Type " +
+ valueComponentType->toString() +
+ " is not implicitly convertible to expected type " +
+ var.annotation().type->toString() +
+ "."
+ );
+ }
}
return false;
}
@@ -799,23 +852,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (!functionType)
{
typeError(_functionCall, "Type is not callable");
- _functionCall.annotation().type = make_shared<VoidType>();
+ _functionCall.annotation().type = make_shared<TupleType>();
return false;
}
+ else if (functionType->returnParameterTypes().size() == 1)
+ _functionCall.annotation().type = functionType->returnParameterTypes().front();
else
- {
- // @todo actually the return type should be an anonymous struct,
- // but we change it to the type of the first return value until we have anonymous
- // structs and tuples
- if (functionType->returnParameterTypes().empty())
- _functionCall.annotation().type = make_shared<VoidType>();
- else
- _functionCall.annotation().type = functionType->returnParameterTypes().front();
- }
+ _functionCall.annotation().type = make_shared<TupleType>(functionType->returnParameterTypes());
- //@todo would be nice to create a struct type from the arguments
- // and then ask if that is implicitly convertible to the struct represented by the
- // function parameters
TypePointers const& parameterTypes = functionType->parameterTypes();
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
{
diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp
index f0c67bba..3d80a163 100644
--- a/libsolidity/Types.cpp
+++ b/libsolidity/Types.cpp
@@ -223,7 +223,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
{
// "delete" is ok for all integer types
if (_operator == Token::Delete)
- return make_shared<VoidType>();
+ return make_shared<TupleType>();
// no further unary operators for addresses
else if (isAddress())
return TypePointer();
@@ -562,7 +562,7 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
{
// "delete" and "~" is okay for FixedBytesType
if (_operator == Token::Delete)
- return make_shared<VoidType>();
+ return make_shared<TupleType>();
else if (_operator == Token::BitNot)
return shared_from_this();
@@ -617,7 +617,7 @@ u256 BoolType::literalValue(Literal const* _literal) const
TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Delete)
- return make_shared<VoidType>();
+ return make_shared<TupleType>();
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
}
@@ -658,7 +658,7 @@ bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
{
- return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
+ return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const
@@ -672,9 +672,9 @@ TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const
case DataLocation::CallData:
return TypePointer();
case DataLocation::Memory:
- return make_shared<VoidType>();
+ return make_shared<TupleType>();
case DataLocation::Storage:
- return m_isPointer ? TypePointer() : make_shared<VoidType>();
+ return m_isPointer ? TypePointer() : make_shared<TupleType>();
default:
solAssert(false, "");
}
@@ -1175,7 +1175,7 @@ set<string> StructType::membersMissingInMemory() const
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
{
- return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
+ return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
bool EnumType::operator==(Type const& _other) const
@@ -1222,6 +1222,40 @@ unsigned int EnumType::memberValue(ASTString const& _member) const
BOOST_THROW_EXCEPTION(m_enum.createTypeError("Requested unknown enum value ." + _member));
}
+bool TupleType::operator==(Type const& _other) const
+{
+ if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
+ return components() == tupleType->components();
+ else
+ return false;
+}
+
+string TupleType::toString(bool _short) const
+{
+ if (m_components.empty())
+ return "tuple()";
+ string str = "tuple(";
+ for (auto const& t: m_components)
+ str += t->toString(_short) + ", ";
+ str.resize(str.size() - 2);
+ return str + ")";
+}
+
+u256 TupleType::storageSize() const
+{
+ BOOST_THROW_EXCEPTION(
+ InternalCompilerError()
+ << errinfo_comment("Storage size of non-storable tuple type requested."));
+}
+
+unsigned TupleType::sizeOnStack() const
+{
+ unsigned size = 0;
+ for (auto const& t: m_components)
+ size += t->sizeOnStack();
+ return size;
+}
+
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::Internal : Location::External),
m_isConstant(_function.isDeclaredConst()),
@@ -1647,13 +1681,6 @@ string MappingType::canonicalName(bool) const
return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")";
}
-u256 VoidType::storageSize() const
-{
- BOOST_THROW_EXCEPTION(
- InternalCompilerError()
- << errinfo_comment("Storage size of non-storable void type requested."));
-}
-
bool TypeType::operator==(Type const& _other) const
{
if (_other.category() != category())
diff --git a/libsolidity/Types.h b/libsolidity/Types.h
index 7a65ca92..e73cd3cd 100644
--- a/libsolidity/Types.h
+++ b/libsolidity/Types.h
@@ -132,8 +132,8 @@ public:
enum class Category
{
Integer, IntegerConstant, StringLiteral, Bool, Real, Array,
- FixedBytes, Contract, Struct, Function, Enum,
- Mapping, Void, TypeType, Modifier, Magic
+ FixedBytes, Contract, Struct, Function, Enum, Tuple,
+ Mapping, TypeType, Modifier, Magic
};
/// @{
@@ -683,6 +683,28 @@ private:
};
/**
+ * Type that can hold a finite sequence of values of different types.
+ */
+class TupleType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Tuple; }
+ explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
+ virtual bool operator==(Type const& _other) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual std::string toString(bool) const override;
+ virtual bool canBeStored() const override { return false; }
+ virtual u256 storageSize() const override;
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned sizeOnStack() const override;
+
+ std::vector<TypePointer> const& components() const { return m_components; }
+
+private:
+ std::vector<TypePointer> const m_components;
+};
+
+/**
* The type of a function, identified by its (return) parameter types.
* @todo the return parameters should also have names, i.e. return parameters should be a struct
* type.
@@ -875,24 +897,6 @@ private:
};
/**
- * The void type, can only be implicitly used as the type that is returned by functions without
- * return parameters.
- */
-class VoidType: public Type
-{
-public:
- virtual Category category() const override { return Category::Void; }
- VoidType() {}
-
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
- virtual std::string toString(bool) const override { return "void"; }
- virtual bool canBeStored() const override { return false; }
- virtual u256 storageSize() const override;
- virtual bool canLiveOutsideStorage() const override { return false; }
- virtual unsigned sizeOnStack() const override { return 0; }
-};
-
-/**
* The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
* of a TypeType.
* For super contracts or libraries, this has members directly.