Disallow multi variable declarations with mismatching number of values.

This commit is contained in:
Daniel Kirchner 2018-07-04 18:34:24 +02:00
parent 5d8a8f7265
commit fc370591f0
6 changed files with 25 additions and 98 deletions

View File

@ -1043,10 +1043,10 @@ namespace
* @returns a suggested left-hand-side of a multi-variable declaration contairing * @returns a suggested left-hand-side of a multi-variable declaration contairing
* the variable declarations given in @a _decls. * the variable declarations given in @a _decls.
*/ */
string createTupleDecl(vector<VariableDeclaration const*> const& _decls) string createTupleDecl(vector<ASTPointer<VariableDeclaration>> const& _decls)
{ {
vector<string> components; vector<string> components;
for (VariableDeclaration const* decl: _decls) for (ASTPointer<VariableDeclaration> const& decl: _decls)
if (decl) if (decl)
components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name()); components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name());
else else
@ -1058,9 +1058,9 @@ string createTupleDecl(vector<VariableDeclaration const*> const& _decls)
return "(" + boost::algorithm::join(components, ", ") + ")"; return "(" + boost::algorithm::join(components, ", ") + ")";
} }
bool typeCanBeExpressed(vector<VariableDeclaration const*> const& decls) bool typeCanBeExpressed(vector<ASTPointer<VariableDeclaration>> const& decls)
{ {
for (VariableDeclaration const* decl: decls) for (ASTPointer<VariableDeclaration> const& decl: decls)
{ {
// skip empty tuples (they can be expressed of course) // skip empty tuples (they can be expressed of course)
if (!decl) if (!decl)
@ -1080,7 +1080,6 @@ bool typeCanBeExpressed(vector<VariableDeclaration const*> const& decls)
bool TypeChecker::visit(VariableDeclarationStatement const& _statement) bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{ {
bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
if (!_statement.initialValue()) if (!_statement.initialValue())
{ {
// No initial value is only permitted for single variables with specified type. // No initial value is only permitted for single variables with specified type.
@ -1119,82 +1118,27 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
else else
valueTypes = TypePointers{type(*_statement.initialValue())}; valueTypes = TypePointers{type(*_statement.initialValue())};
// Determine which component is assigned to which variable.
// If numbers do not match, fill up if variables begin or end empty (not both).
vector<VariableDeclaration const*>& assignments = _statement.annotation().assignments;
assignments.resize(valueTypes.size(), nullptr);
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations(); vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
if (variables.empty()) if (variables.empty())
{ // We already have an error for this in the SyntaxChecker.
if (!valueTypes.empty()) solAssert(m_errorReporter.hasErrors(), "");
m_errorReporter.fatalTypeError(
_statement.location(),
"Too many components (" +
toString(valueTypes.size()) +
") in value for variable assignment (0) needed"
);
}
else if (valueTypes.size() != variables.size()) else if (valueTypes.size() != variables.size())
{ m_errorReporter.typeError(
if (v050)
m_errorReporter.fatalTypeError(
_statement.location(),
"Different number of components on the left hand side (" +
toString(variables.size()) +
") than on the right hand side (" +
toString(valueTypes.size()) +
")."
);
else if (!variables.front() && !variables.back())
m_errorReporter.fatalTypeError(
_statement.location(),
"Wildcard both at beginning and end of variable declaration list is only allowed "
"if the number of components is equal."
);
else
m_errorReporter.warning(
_statement.location(),
"Different number of components on the left hand side (" +
toString(variables.size()) +
") than on the right hand side (" +
toString(valueTypes.size()) +
")."
);
}
size_t minNumValues = variables.size();
if (!variables.empty() && (!variables.back() || !variables.front()))
--minNumValues;
if (valueTypes.size() < minNumValues)
m_errorReporter.fatalTypeError(
_statement.location(), _statement.location(),
"Not enough components (" + "Different number of components on the left hand side (" +
toString(variables.size()) +
") than on the right hand side (" +
toString(valueTypes.size()) + toString(valueTypes.size()) +
") in value to assign all variables (" + ")."
toString(minNumValues) + ")."
); );
if (valueTypes.size() > variables.size() && variables.front() && variables.back())
m_errorReporter.fatalTypeError(
_statement.location(),
"Too many components (" +
toString(valueTypes.size()) +
") in value for variable assignment (" +
toString(minNumValues) +
" needed)."
);
bool fillRight = !variables.empty() && (!variables.back() || variables.front());
for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
if (fillRight)
assignments[i] = variables[i].get();
else
assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get();
bool autoTypeDeductionNeeded = false; bool autoTypeDeductionNeeded = false;
for (size_t i = 0; i < assignments.size(); ++i) for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
{ {
if (!assignments[i]) if (!variables[i])
continue; continue;
VariableDeclaration const& var = *assignments[i]; VariableDeclaration const& var = *variables[i];
solAssert(!var.value(), "Value has to be tied to statement."); solAssert(!var.value(), "Value has to be tied to statement.");
TypePointer const& valueComponentType = valueTypes[i]; TypePointer const& valueComponentType = valueTypes[i];
solAssert(!!valueComponentType, ""); solAssert(!!valueComponentType, "");
@ -1284,7 +1228,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (autoTypeDeductionNeeded) if (autoTypeDeductionNeeded)
{ {
if (!typeCanBeExpressed(assignments)) if (!typeCanBeExpressed(variables))
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
_statement.location(), _statement.location(),
"Use of the \"var\" keyword is disallowed. " "Use of the \"var\" keyword is disallowed. "
@ -1294,7 +1238,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
_statement.location(), _statement.location(),
"Use of the \"var\" keyword is disallowed. " "Use of the \"var\" keyword is disallowed. "
"Use explicit declaration `" + createTupleDecl(assignments) + " = ...´ instead." "Use explicit declaration `" + createTupleDecl(variables) + " = ...´ instead."
); );
} }

View File

@ -535,13 +535,6 @@ ReturnAnnotation& Return::annotation() const
return dynamic_cast<ReturnAnnotation&>(*m_annotation); return dynamic_cast<ReturnAnnotation&>(*m_annotation);
} }
VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const
{
if (!m_annotation)
m_annotation = new VariableDeclarationStatementAnnotation();
return dynamic_cast<VariableDeclarationStatementAnnotation&>(*m_annotation);
}
ExpressionAnnotation& Expression::annotation() const ExpressionAnnotation& Expression::annotation() const
{ {
if (!m_annotation) if (!m_annotation)

View File

@ -1270,8 +1270,6 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
VariableDeclarationStatementAnnotation& annotation() const override;
std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; } std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
Expression const* initialValue() const { return m_initialValue.get(); } Expression const* initialValue() const { return m_initialValue.get(); }

View File

@ -164,13 +164,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
ContractDefinition const* contractScope = nullptr; ContractDefinition const* contractScope = nullptr;
}; };
struct VariableDeclarationStatementAnnotation: StatementAnnotation
{
/// Information about which component of the value is assigned to which variable.
/// The pointer can be null to signify that the component is discarded.
std::vector<VariableDeclaration const*> assignments;
};
struct ExpressionAnnotation: ASTAnnotation struct ExpressionAnnotation: ASTAnnotation
{ {
/// Inferred type of the expression. /// Inferred type of the expression.

View File

@ -555,8 +555,8 @@ bool ASTJsonConverter::visit(EmitStatement const& _node)
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
{ {
Json::Value varDecs(Json::arrayValue); Json::Value varDecs(Json::arrayValue);
for (auto const& v: _node.annotation().assignments) for (auto const& v: _node.declarations())
appendMove(varDecs, idOrNull(v)); appendMove(varDecs, idOrNull(v.get()));
setJsonNode(_node, "VariableDeclarationStatement", { setJsonNode(_node, "VariableDeclarationStatement", {
make_pair("assignments", std::move(varDecs)), make_pair("assignments", std::move(varDecs)),
make_pair("declarations", toJson(_node.declarations())), make_pair("declarations", toJson(_node.declarations())),

View File

@ -833,20 +833,19 @@ bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclar
valueTypes = tupleType->components(); valueTypes = tupleType->components();
else else
valueTypes = TypePointers{expression->annotation().type}; valueTypes = TypePointers{expression->annotation().type};
auto const& assignments = _variableDeclarationStatement.annotation().assignments; auto const& declarations = _variableDeclarationStatement.declarations();
solAssert(assignments.size() == valueTypes.size(), ""); solAssert(declarations.size() == valueTypes.size(), "");
for (size_t i = 0; i < assignments.size(); ++i) for (size_t i = 0; i < declarations.size(); ++i)
{ {
size_t j = assignments.size() - i - 1; size_t j = declarations.size() - i - 1;
solAssert(!!valueTypes[j], ""); solAssert(!!valueTypes[j], "");
VariableDeclaration const* varDecl = assignments[j]; if (VariableDeclaration const* varDecl = declarations[j].get())
if (!varDecl)
utils.popStackElement(*valueTypes[j]);
else
{ {
utils.convertType(*valueTypes[j], *varDecl->annotation().type); utils.convertType(*valueTypes[j], *varDecl->annotation().type);
utils.moveToStackVariable(*varDecl); utils.moveToStackVariable(*varDecl);
} }
else
utils.popStackElement(*valueTypes[j]);
} }
} }
checker.check(); checker.check();