aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/control-structures.rst4
-rw-r--r--docs/frequently-asked-questions.rst8
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp4
-rw-r--r--libsolidity/analysis/SemVerHandler.cpp4
-rw-r--r--libsolidity/analysis/SemVerHandler.h8
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp14
-rw-r--r--libsolidity/ast/AST.h58
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp12
-rw-r--r--libsolidity/ast/ASTJsonConverter.h2
-rw-r--r--libsolidity/ast/ASTPrinter.cpp8
-rw-r--r--libsolidity/ast/Types.cpp64
-rw-r--r--libsolidity/ast/Types.h58
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp40
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h12
-rw-r--r--libsolidity/formal/SMTChecker.cpp12
-rw-r--r--libsolidity/parsing/Parser.cpp80
-rw-r--r--libsolidity/parsing/ParserBase.cpp20
-rw-r--r--libsolidity/parsing/ParserBase.h8
-rw-r--r--libsolidity/parsing/Scanner.cpp32
-rw-r--r--libsolidity/parsing/Scanner.h30
-rw-r--r--libsolidity/parsing/Token.cpp98
-rw-r--r--libsolidity/parsing/Token.h145
-rw-r--r--libyul/optimiser/RedundantAssignEliminator.cpp193
-rw-r--r--libyul/optimiser/RedundantAssignEliminator.h185
-rw-r--r--test/libsolidity/SemVerMatcher.cpp6
-rw-r--r--test/libyul/YulOptimizerTest.cpp13
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul26
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul31
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul27
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul23
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul24
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul24
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul25
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul19
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul15
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul11
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul16
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul10
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul22
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul19
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul23
-rw-r--r--test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul16
-rw-r--r--test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul35
-rw-r--r--test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul17
-rw-r--r--test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul17
46 files changed, 1150 insertions, 370 deletions
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index c2d60df1..9da29d14 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -126,6 +126,10 @@ actual contract has not been created yet.
Functions of other contracts have to be called externally. For an external call,
all function arguments have to be copied to memory.
+.. note::
+ A function call from one contract to another does not create its own transaction,
+ it is a message call as part of the overall transaction.
+
When calling functions of other contracts, the amount of Wei sent with the call and
the gas can be specified with special options ``.value()`` and ``.gas()``, respectively::
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index 0f8b34f8..3e9a6aca 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -213,14 +213,6 @@ It gets added to the total balance of the contract, just like when you send ethe
You can only send ether along to a function that has the ``payable`` modifier,
otherwise an exception is thrown.
-Is it possible to get a tx receipt for a transaction executed contract-to-contract?
-===================================================================================
-
-No, a function call from one contract to another does not create its own transaction,
-you have to look in the overall transaction. This is also the reason why several
-block explorer do not show Ether sent between contracts correctly.
-
-
******************
Advanced Questions
******************
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
index 8659bbfd..f9b00927 100644
--- a/libsolidity/analysis/ConstantEvaluator.cpp
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -46,7 +46,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
m_errorReporter.fatalTypeError(
_operation.location(),
"Operator " +
- string(Token::toString(_operation.getOperator())) +
+ string(TokenTraits::toString(_operation.getOperator())) +
" not compatible with types " +
left->toString() +
" and " +
@@ -54,7 +54,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
);
setType(
_operation,
- Token::isCompareOp(_operation.getOperator()) ?
+ TokenTraits::isCompareOp(_operation.getOperator()) ?
make_shared<BoolType>() :
commonType
);
diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp
index 29f6d5de..64fa17b3 100644
--- a/libsolidity/analysis/SemVerHandler.cpp
+++ b/libsolidity/analysis/SemVerHandler.cpp
@@ -199,7 +199,7 @@ void SemVerMatchExpressionParser::parseMatchExpression()
SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchComponent()
{
SemVerMatchExpression::MatchComponent component;
- Token::Value token = currentToken();
+ Token token = currentToken();
switch (token)
{
@@ -280,7 +280,7 @@ char SemVerMatchExpressionParser::nextChar()
return currentChar();
}
-Token::Value SemVerMatchExpressionParser::currentToken() const
+Token SemVerMatchExpressionParser::currentToken() const
{
if (m_pos < m_tokens.size())
return m_tokens[m_pos];
diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h
index 76b70c5b..03a557c5 100644
--- a/libsolidity/analysis/SemVerHandler.h
+++ b/libsolidity/analysis/SemVerHandler.h
@@ -61,7 +61,7 @@ struct SemVerMatchExpression
struct MatchComponent
{
/// Prefix from < > <= >= ~ ^
- Token::Value prefix = Token::Illegal;
+ Token prefix = Token::Illegal;
/// Version, where unsigned(-1) in major, minor or patch denotes '*', 'x' or 'X'
SemVerVersion version;
/// Whether we have 1, 1.2 or 1.2.4
@@ -81,7 +81,7 @@ struct SemVerMatchExpression
class SemVerMatchExpressionParser
{
public:
- SemVerMatchExpressionParser(std::vector<Token::Value> const& _tokens, std::vector<std::string> const& _literals):
+ SemVerMatchExpressionParser(std::vector<Token> const& _tokens, std::vector<std::string> const& _literals):
m_tokens(_tokens), m_literals(_literals)
{}
SemVerMatchExpression parse();
@@ -95,10 +95,10 @@ private:
char currentChar() const;
char nextChar();
- Token::Value currentToken() const;
+ Token currentToken() const;
void nextToken();
- std::vector<Token::Value> m_tokens;
+ std::vector<Token> m_tokens;
std::vector<std::string> m_literals;
unsigned m_pos = 0;
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index ab544388..3f9f8373 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -106,7 +106,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
}
else if (_pragma.literals()[0] == "solidity")
{
- vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
+ vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
SemVerMatchExpressionParser parser(tokens, literals);
auto matchExpression = parser.parse();
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 3830935f..3774cf86 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1499,14 +1499,14 @@ bool TypeChecker::visit(Assignment const& _assignment)
// compound assignment
_assignment.rightHandSide().accept(*this);
TypePointer resultType = t->binaryOperatorResult(
- Token::AssignmentToBinaryOp(_assignment.assignmentOperator()),
+ TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()),
type(_assignment.rightHandSide())
);
if (!resultType || *resultType != *t)
m_errorReporter.typeError(
_assignment.location(),
"Operator " +
- string(Token::toString(_assignment.assignmentOperator())) +
+ string(TokenTraits::toString(_assignment.assignmentOperator())) +
" not compatible with types " +
t->toString() +
" and " +
@@ -1607,8 +1607,8 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
bool TypeChecker::visit(UnaryOperation const& _operation)
{
// Inc, Dec, Add, Sub, Not, BitNot, Delete
- Token::Value op = _operation.getOperator();
- bool const modifying = (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete);
+ Token op = _operation.getOperator();
+ bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete);
if (modifying)
requireLValue(_operation.subExpression());
else
@@ -1620,7 +1620,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
m_errorReporter.typeError(
_operation.location(),
"Unary operator " +
- string(Token::toString(op)) +
+ string(TokenTraits::toString(op)) +
" cannot be applied to type " +
subExprType->toString()
);
@@ -1641,7 +1641,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
m_errorReporter.typeError(
_operation.location(),
"Operator " +
- string(Token::toString(_operation.getOperator())) +
+ string(TokenTraits::toString(_operation.getOperator())) +
" not compatible with types " +
leftType->toString() +
" and " +
@@ -1651,7 +1651,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
}
_operation.annotation().commonType = commonType;
_operation.annotation().type =
- Token::isCompareOp(_operation.getOperator()) ?
+ TokenTraits::isCompareOp(_operation.getOperator()) ?
make_shared<BoolType>() :
commonType;
_operation.annotation().isPure =
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index b84f9730..d819402e 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -237,7 +237,7 @@ class PragmaDirective: public ASTNode
public:
PragmaDirective(
SourceLocation const& _location,
- std::vector<Token::Value> const& _tokens,
+ std::vector<Token> const& _tokens,
std::vector<ASTString> const& _literals
): ASTNode(_location), m_tokens(_tokens), m_literals(_literals)
{}
@@ -245,13 +245,13 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- std::vector<Token::Value> const& tokens() const { return m_tokens; }
+ std::vector<Token> const& tokens() const { return m_tokens; }
std::vector<ASTString> const& literals() const { return m_literals; }
private:
/// Sequence of tokens following the "pragma" keyword.
- std::vector<Token::Value> m_tokens;
+ std::vector<Token> m_tokens;
/// Sequence of literals following the "pragma" keyword.
std::vector<ASTString> m_literals;
};
@@ -1379,7 +1379,7 @@ public:
Assignment(
SourceLocation const& _location,
ASTPointer<Expression> const& _leftHandSide,
- Token::Value _assignmentOperator,
+ Token _assignmentOperator,
ASTPointer<Expression> const& _rightHandSide
):
Expression(_location),
@@ -1387,18 +1387,18 @@ public:
m_assigmentOperator(_assignmentOperator),
m_rightHandSide(_rightHandSide)
{
- solAssert(Token::isAssignmentOp(_assignmentOperator), "");
+ solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
Expression const& leftHandSide() const { return *m_leftHandSide; }
- Token::Value assignmentOperator() const { return m_assigmentOperator; }
+ Token assignmentOperator() const { return m_assigmentOperator; }
Expression const& rightHandSide() const { return *m_rightHandSide; }
private:
ASTPointer<Expression> m_leftHandSide;
- Token::Value m_assigmentOperator;
+ Token m_assigmentOperator;
ASTPointer<Expression> m_rightHandSide;
};
@@ -1441,7 +1441,7 @@ class UnaryOperation: public Expression
public:
UnaryOperation(
SourceLocation const& _location,
- Token::Value _operator,
+ Token _operator,
ASTPointer<Expression> const& _subExpression,
bool _isPrefix
):
@@ -1450,17 +1450,17 @@ public:
m_subExpression(_subExpression),
m_isPrefix(_isPrefix)
{
- solAssert(Token::isUnaryOp(_operator), "");
+ solAssert(TokenTraits::isUnaryOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- Token::Value getOperator() const { return m_operator; }
+ Token getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; }
Expression const& subExpression() const { return *m_subExpression; }
private:
- Token::Value m_operator;
+ Token m_operator;
ASTPointer<Expression> m_subExpression;
bool m_isPrefix;
};
@@ -1475,25 +1475,25 @@ public:
BinaryOperation(
SourceLocation const& _location,
ASTPointer<Expression> const& _left,
- Token::Value _operator,
+ Token _operator,
ASTPointer<Expression> const& _right
):
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right)
{
- solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), "");
+ solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
Expression const& leftExpression() const { return *m_left; }
Expression const& rightExpression() const { return *m_right; }
- Token::Value getOperator() const { return m_operator; }
+ Token getOperator() const { return m_operator; }
BinaryOperationAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_left;
- Token::Value m_operator;
+ Token m_operator;
ASTPointer<Expression> m_right;
};
@@ -1653,21 +1653,21 @@ class Literal: public PrimaryExpression
public:
enum class SubDenomination
{
- None = Token::Illegal,
- Wei = Token::SubWei,
- Szabo = Token::SubSzabo,
- Finney = Token::SubFinney,
- Ether = Token::SubEther,
- Second = Token::SubSecond,
- Minute = Token::SubMinute,
- Hour = Token::SubHour,
- Day = Token::SubDay,
- Week = Token::SubWeek,
- Year = Token::SubYear
+ None = static_cast<int>(Token::Illegal),
+ Wei = static_cast<int>(Token::SubWei),
+ Szabo = static_cast<int>(Token::SubSzabo),
+ Finney = static_cast<int>(Token::SubFinney),
+ Ether = static_cast<int>(Token::SubEther),
+ Second = static_cast<int>(Token::SubSecond),
+ Minute = static_cast<int>(Token::SubMinute),
+ Hour = static_cast<int>(Token::SubHour),
+ Day = static_cast<int>(Token::SubDay),
+ Week = static_cast<int>(Token::SubWeek),
+ Year = static_cast<int>(Token::SubYear)
};
Literal(
SourceLocation const& _location,
- Token::Value _token,
+ Token _token,
ASTPointer<ASTString> const& _value,
SubDenomination _sub = SubDenomination::None
):
@@ -1675,7 +1675,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- Token::Value token() const { return m_token; }
+ Token token() const { return m_token; }
/// @returns the non-parsed value of the literal
ASTString const& value() const { return *m_value; }
@@ -1694,7 +1694,7 @@ public:
std::string getChecksummedAddress() const;
private:
- Token::Value m_token;
+ Token m_token;
ASTPointer<ASTString> m_value;
SubDenomination m_subDenomination;
};
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 4b282d85..569e5b0e 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -589,7 +589,7 @@ bool ASTJsonConverter::visit(Conditional const& _node)
bool ASTJsonConverter::visit(Assignment const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
- make_pair("operator", Token::toString(_node.assignmentOperator())),
+ make_pair("operator", TokenTraits::toString(_node.assignmentOperator())),
make_pair("leftHandSide", toJson(_node.leftHandSide())),
make_pair("rightHandSide", toJson(_node.rightHandSide()))
};
@@ -613,7 +613,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("prefix", _node.isPrefixOperation()),
- make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("operator", TokenTraits::toString(_node.getOperator())),
make_pair("subExpression", toJson(_node.subExpression()))
};
appendExpressionAttributes(attributes, _node.annotation());
@@ -624,7 +624,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node)
bool ASTJsonConverter::visit(BinaryOperation const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
- make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("operator", TokenTraits::toString(_node.getOperator())),
make_pair("leftExpression", toJson(_node.leftExpression())),
make_pair("rightExpression", toJson(_node.rightExpression())),
make_pair("commonType", typePointerToJson(_node.annotation().commonType)),
@@ -719,7 +719,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
Json::Value value{_node.value()};
if (!dev::validateUTF8(_node.value()))
value = Json::nullValue;
- Token::Value subdenomination = Token::Value(_node.subDenomination());
+ Token subdenomination = Token(_node.subDenomination());
std::vector<pair<string, Json::Value>> attributes = {
make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
make_pair("value", value),
@@ -728,7 +728,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
"subdenomination",
subdenomination == Token::Illegal ?
Json::nullValue :
- Json::Value{Token::toString(subdenomination)}
+ Json::Value{TokenTraits::toString(subdenomination)}
)
};
appendExpressionAttributes(attributes, _node.annotation());
@@ -790,7 +790,7 @@ string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
}
}
-string ASTJsonConverter::literalTokenKind(Token::Value _token)
+string ASTJsonConverter::literalTokenKind(Token _token)
{
switch (_token)
{
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index 29712f3b..8429708c 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -134,7 +134,7 @@ private:
static std::string location(VariableDeclaration::Location _location);
static std::string contractKind(ContractDefinition::ContractKind _kind);
static std::string functionCallKind(FunctionCallKind _kind);
- static std::string literalTokenKind(Token::Value _token);
+ static std::string literalTokenKind(Token _token);
static std::string type(Expression const& _expression);
static std::string type(VariableDeclaration const& _varDecl);
static int nodeId(ASTNode const& _node)
diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp
index 4273f225..255cb9be 100644
--- a/libsolidity/ast/ASTPrinter.cpp
+++ b/libsolidity/ast/ASTPrinter.cpp
@@ -289,7 +289,7 @@ bool ASTPrinter::visit(Conditional const& _node)
bool ASTPrinter::visit(Assignment const& _node)
{
- writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator()));
+ writeLine(string("Assignment using operator ") + TokenTraits::toString(_node.assignmentOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -306,7 +306,7 @@ bool ASTPrinter::visit(TupleExpression const& _node)
bool ASTPrinter::visit(UnaryOperation const& _node)
{
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
- ") " + Token::toString(_node.getOperator()));
+ ") " + TokenTraits::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -314,7 +314,7 @@ bool ASTPrinter::visit(UnaryOperation const& _node)
bool ASTPrinter::visit(BinaryOperation const& _node)
{
- writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
+ writeLine(string("BinaryOperation using operator ") + TokenTraits::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -370,7 +370,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node)
bool ASTPrinter::visit(Literal const& _node)
{
- char const* tokenString = Token::toString(_node.token());
+ char const* tokenString = TokenTraits::toString(_node.token());
if (!tokenString)
tokenString = "[no token]";
writeLine(string("Literal, token: ") + tokenString + " value: " + _node.value());
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 195b2e2d..301687b4 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -268,11 +268,11 @@ string Type::identifier() const
TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
{
- solAssert(Token::isElementaryTypeName(_type.token()),
+ solAssert(TokenTraits::isElementaryTypeName(_type.token()),
"Expected an elementary type name but got " + _type.toString()
);
- Token::Value token = _type.token();
+ Token token = _type.token();
unsigned m = _type.firstNumber();
unsigned n = _type.secondNumber();
@@ -320,9 +320,9 @@ TypePointer Type::fromElementaryTypeName(string const& _name)
vector<string> nameParts;
boost::split(nameParts, _name, boost::is_any_of(" "));
solAssert(nameParts.size() == 1 || nameParts.size() == 2, "Cannot parse elementary type: " + _name);
- Token::Value token;
+ Token token;
unsigned short firstNum, secondNum;
- tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(nameParts[0]);
+ tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(nameParts[0]);
auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum));
if (auto* ref = dynamic_cast<ReferenceType const*>(t.get()))
{
@@ -502,16 +502,16 @@ u256 AddressType::literalValue(Literal const* _literal) const
return u256(_literal->valueWithoutUnderscores());
}
-TypePointer AddressType::unaryOperatorResult(Token::Value _operator) const
+TypePointer AddressType::unaryOperatorResult(Token _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
-TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
// Addresses can only be compared.
- if (!Token::isCompareOp(_operator))
+ if (!TokenTraits::isCompareOp(_operator))
return TypePointer();
return Type::commonType(shared_from_this(), _other);
@@ -545,7 +545,7 @@ MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) cons
namespace
{
-bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountType)
+bool isValidShiftAndAmountType(Token _operator, Type const& _shiftAmountType)
{
// Disable >>> here.
if (_operator == Token::SHR)
@@ -605,7 +605,7 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == Category::FixedPoint;
}
-TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
+TypePointer IntegerType::unaryOperatorResult(Token _operator) const
{
// "delete" is ok for all integer types
if (_operator == Token::Delete)
@@ -649,7 +649,7 @@ bigint IntegerType::maxValue() const
return (bigint(1) << m_bits) - 1;
}
-TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (
_other->category() != Category::RationalNumber &&
@@ -657,7 +657,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
_other->category() != category()
)
return TypePointer();
- if (Token::isShiftOp(_operator))
+ if (TokenTraits::isShiftOp(_operator))
{
// Shifts are not symmetric with respect to the type
if (isValidShiftAndAmountType(_operator, *_other))
@@ -671,9 +671,9 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
return TypePointer();
// All integer types can be compared
- if (Token::isCompareOp(_operator))
+ if (TokenTraits::isCompareOp(_operator))
return commonType;
- if (Token::isBooleanOp(_operator))
+ if (TokenTraits::isBooleanOp(_operator))
return TypePointer();
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
{
@@ -720,7 +720,7 @@ bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
}
-TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const
+TypePointer FixedPointType::unaryOperatorResult(Token _operator) const
{
switch(_operator)
{
@@ -769,7 +769,7 @@ bigint FixedPointType::minIntegerValue() const
return bigint(0);
}
-TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
auto commonType = Type::commonType(shared_from_this(), _other);
@@ -777,9 +777,9 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi
return TypePointer();
// All fixed types can be compared
- if (Token::isCompareOp(_operator))
+ if (TokenTraits::isCompareOp(_operator))
return commonType;
- if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator) || _operator == Token::Exp)
+ if (TokenTraits::isBitOp(_operator) || TokenTraits::isBooleanOp(_operator) || _operator == Token::Exp)
return TypePointer();
return commonType;
}
@@ -1006,7 +1006,7 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const
+TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
{
rational value;
switch (_operator)
@@ -1030,7 +1030,7 @@ TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) cons
return make_shared<RationalNumberType>(value);
}
-TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{
@@ -1043,7 +1043,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
return TypePointer();
RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other);
- if (Token::isCompareOp(_operator))
+ if (TokenTraits::isCompareOp(_operator))
{
// Since we do not have a "BoolConstantType", we have to do the actual comparison
// at runtime and convert to mobile typse first. Such a comparison is not a very common
@@ -1423,7 +1423,7 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == category();
}
-TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
+TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const
{
// "delete" and "~" is okay for FixedBytesType
if (_operator == Token::Delete)
@@ -1434,9 +1434,9 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
return TypePointer();
}
-TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
- if (Token::isShiftOp(_operator))
+ if (TokenTraits::isShiftOp(_operator))
{
if (isValidShiftAndAmountType(_operator, *_other))
return shared_from_this();
@@ -1449,7 +1449,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePoi
return TypePointer();
// FixedBytes can be compared and have bitwise operators applied to them
- if (Token::isCompareOp(_operator) || Token::isBitOp(_operator))
+ if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator))
return commonType;
return TypePointer();
@@ -1484,14 +1484,14 @@ u256 BoolType::literalValue(Literal const* _literal) const
solAssert(false, "Bool type constructed from non-boolean literal.");
}
-TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
+TypePointer BoolType::unaryOperatorResult(Token _operator) const
{
if (_operator == Token::Delete)
return make_shared<TupleType>();
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
}
-TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (category() != _other->category())
return TypePointer();
@@ -1531,14 +1531,14 @@ bool ContractType::isPayable() const
return fallbackFunction && fallbackFunction->isPayable();
}
-TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
+TypePointer ContractType::unaryOperatorResult(Token _operator) const
{
if (isSuper())
return TypePointer{};
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
-TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const
+TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
{
if (_operator != Token::Delete)
return TypePointer();
@@ -2237,7 +2237,7 @@ bool StructType::recursive() const
return *m_recursive;
}
-TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
+TypePointer EnumType::unaryOperatorResult(Token _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
@@ -2668,14 +2668,14 @@ bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return true;
}
-TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
+TypePointer FunctionType::unaryOperatorResult(Token _operator) const
{
- if (_operator == Token::Value::Delete)
+ if (_operator == Token::Delete)
return make_shared<TupleType>();
return TypePointer();
}
-TypePointer FunctionType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
return TypePointer();
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 0f3373a1..b764717f 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -188,13 +188,13 @@ public:
/// @returns the resulting type of applying the given unary operator or an empty pointer if
/// this is not possible.
/// The default implementation does not allow any unary operator.
- virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); }
+ virtual TypePointer unaryOperatorResult(Token) const { return TypePointer(); }
/// @returns the resulting type of applying the given binary operator or an empty pointer if
/// this is not possible.
/// The default implementation allows comparison operators if a common type exists
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
- return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
+ return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
}
virtual bool operator==(Type const& _other) const { return category() == _other.category(); }
@@ -329,8 +329,8 @@ public:
virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual bool operator==(Type const& _other) const override;
@@ -372,8 +372,8 @@ public:
virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual bool operator==(Type const& _other) const override;
@@ -414,8 +414,8 @@ public:
virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual bool operator==(Type const& _other) const override;
@@ -468,8 +468,8 @@ public:
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
@@ -526,7 +526,7 @@ public:
explicit StringLiteralType(Literal const& _literal);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -563,8 +563,8 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
virtual unsigned storageBytes() const override { return m_bytes; }
@@ -590,8 +590,8 @@ public:
BoolType() {}
virtual Category category() const override { return Category::Bool; }
virtual std::string richIdentifier() const override { return "t_bool"; }
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
virtual unsigned storageBytes() const override { return 1; }
@@ -613,8 +613,8 @@ public:
explicit ReferenceType(DataLocation _location): m_location(_location) {}
DataLocation location() const { return m_location; }
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -749,7 +749,7 @@ public:
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can only be explicitly converted to address types and base contracts.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded ) const override
@@ -865,7 +865,7 @@ class EnumType: public Type
public:
virtual Category category() const override { return Category::Enum; }
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override
@@ -909,7 +909,7 @@ public:
virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, 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;
@@ -1056,8 +1056,8 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override;
virtual std::string canonicalName() const override;
virtual std::string toString(bool _short) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override;
@@ -1187,7 +1187,7 @@ public:
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(256);
@@ -1220,7 +1220,7 @@ public:
explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
TypePointer const& actualType() const { return m_actualType; }
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
@@ -1245,7 +1245,7 @@ public:
virtual Category category() const override { return Category::Modifier; }
explicit ModifierType(ModifierDefinition const& _modifier);
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual bool canBeStored() const override { return false; }
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
@@ -1271,7 +1271,7 @@ public:
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
@@ -1298,7 +1298,7 @@ public:
explicit MagicType(Kind _kind): m_kind(_kind) {}
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -1331,7 +1331,7 @@ public:
virtual std::string richIdentifier() const override { return "t_inaccessible"; }
virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return false; }
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 749739ce..63faddd3 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -206,8 +206,8 @@ bool ExpressionCompiler::visit(Conditional const& _condition)
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
- Token::Value op = _assignment.assignmentOperator();
- Token::Value binOp = op == Token::Assign ? op : Token::AssignmentToBinaryOp(op);
+ Token op = _assignment.assignmentOperator();
+ Token binOp = op == Token::Assign ? op : TokenTraits::AssignmentToBinaryOp(op);
Type const& leftType = *_assignment.leftHandSide().annotation().type;
if (leftType.category() == Type::Category::Tuple)
{
@@ -223,7 +223,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
// Perform some conversion already. This will convert storage types to memory and literals
// to their actual type, but will not convert e.g. memory to storage.
TypePointer rightIntermediateType;
- if (op != Token::Assign && Token::isShiftOp(binOp))
+ if (op != Token::Assign && TokenTraits::isShiftOp(binOp))
rightIntermediateType = _assignment.rightHandSide().annotation().type->mobileType();
else
rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType(
@@ -251,7 +251,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
m_currentLValue->retrieveValue(_assignment.location(), true);
utils().convertType(leftType, leftType, cleanupNeeded);
- if (Token::isShiftOp(binOp))
+ if (TokenTraits::isShiftOp(binOp))
appendShiftOperatorCode(binOp, leftType, *rightIntermediateType);
else
{
@@ -384,7 +384,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << u256(0) << Instruction::SUB;
break;
default:
- solAssert(false, "Invalid unary operator: " + string(Token::toString(_unaryOperation.getOperator())));
+ solAssert(false, "Invalid unary operator: " + string(TokenTraits::toString(_unaryOperation.getOperator())));
}
return false;
}
@@ -396,7 +396,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
Expression const& rightExpression = _binaryOperation.rightExpression();
solAssert(!!_binaryOperation.annotation().commonType, "");
TypePointer const& commonType = _binaryOperation.annotation().commonType;
- Token::Value const c_op = _binaryOperation.getOperator();
+ Token const c_op = _binaryOperation.getOperator();
if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
@@ -407,7 +407,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op);
TypePointer leftTargetType = commonType;
- TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType;
+ TypePointer rightTargetType = TokenTraits::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType;
solAssert(rightTargetType, "");
// for commutative operators, push the literal as late as possible to allow improved optimization
@@ -415,7 +415,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
{
return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber;
};
- bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
+ bool swap = m_optimize && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
if (swap)
{
leftExpression.accept(*this);
@@ -430,10 +430,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
leftExpression.accept(*this);
utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded);
}
- if (Token::isShiftOp(c_op))
+ if (TokenTraits::isShiftOp(c_op))
// shift only cares about the signedness of both sides
appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType);
- else if (Token::isCompareOp(c_op))
+ else if (TokenTraits::isCompareOp(c_op))
appendCompareOperatorCode(c_op, *commonType);
else
appendOrdinaryBinaryOperatorCode(c_op, *commonType);
@@ -1602,7 +1602,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{
- Token::Value const c_op = _binaryOperation.getOperator();
+ Token const c_op = _binaryOperation.getOperator();
solAssert(c_op == Token::Or || c_op == Token::And, "");
_binaryOperation.leftExpression().accept(*this);
@@ -1615,7 +1615,7 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO
m_context << endLabel;
}
-void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
+void ExpressionCompiler::appendCompareOperatorCode(Token _operator, Type const& _type)
{
solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types.");
if (_operator == Token::Equal || _operator == Token::NotEqual)
@@ -1665,17 +1665,17 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
}
}
-void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type)
+void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token _operator, Type const& _type)
{
- if (Token::isArithmeticOp(_operator))
+ if (TokenTraits::isArithmeticOp(_operator))
appendArithmeticOperatorCode(_operator, _type);
- else if (Token::isBitOp(_operator))
+ else if (TokenTraits::isBitOp(_operator))
appendBitOperatorCode(_operator);
else
solAssert(false, "Unknown binary operator.");
}
-void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
+void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type const& _type)
{
if (_type.category() == Type::Category::FixedPoint)
solUnimplemented("Not yet implemented - FixedPointType.");
@@ -1715,7 +1715,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
}
}
-void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
+void ExpressionCompiler::appendBitOperatorCode(Token _operator)
{
switch (_operator)
{
@@ -1733,7 +1733,7 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
}
}
-void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType)
+void ExpressionCompiler::appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType)
{
// stack: shift_amount value_to_shift
@@ -2140,9 +2140,9 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
setLValue<StorageItem>(_expression, *_expression.annotation().type);
}
-bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op)
+bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op)
{
- if (Token::isCompareOp(_op) || Token::isShiftOp(_op))
+ if (TokenTraits::isCompareOp(_op) || TokenTraits::isShiftOp(_op))
return true;
else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp))
// We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index cdfa096e..3d8e8682 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -86,12 +86,12 @@ private:
///@{
///@name Append code for various operator types
void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation);
- void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
- void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
+ void appendCompareOperatorCode(Token _operator, Type const& _type);
+ void appendOrdinaryBinaryOperatorCode(Token _operator, Type const& _type);
- void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
- void appendBitOperatorCode(Token::Value _operator);
- void appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType);
+ void appendArithmeticOperatorCode(Token _operator, Type const& _type);
+ void appendBitOperatorCode(Token _operator);
+ void appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType);
/// @}
/// Appends code to call a function of the given type with the given arguments.
@@ -119,7 +119,7 @@ private:
/// @returns true if the operator applied to the given type requires a cleanup prior to the
/// operation.
- static bool cleanupNeededForOp(Type::Category _type, Token::Value _op);
+ static bool cleanupNeededForOp(Type::Category _type, Token _op);
/// @returns the CompilerUtils object containing the current context.
CompilerUtils utils();
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index 03ec7fac..631a9eee 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -212,7 +212,7 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl)
void SMTChecker::endVisit(Assignment const& _assignment)
{
- if (_assignment.assignmentOperator() != Token::Value::Assign)
+ if (_assignment.assignmentOperator() != Token::Assign)
m_errorReporter.warning(
_assignment.location(),
"Assertion checker does not yet implement compound assignment."
@@ -331,11 +331,11 @@ void SMTChecker::endVisit(UnaryOperation const& _op)
void SMTChecker::endVisit(BinaryOperation const& _op)
{
- if (Token::isArithmeticOp(_op.getOperator()))
+ if (TokenTraits::isArithmeticOp(_op.getOperator()))
arithmeticOperation(_op);
- else if (Token::isCompareOp(_op.getOperator()))
+ else if (TokenTraits::isCompareOp(_op.getOperator()))
compareOperation(_op);
- else if (Token::isBooleanOp(_op.getOperator()))
+ else if (TokenTraits::isBooleanOp(_op.getOperator()))
booleanOperation(_op);
else
m_errorReporter.warning(
@@ -610,7 +610,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op)
auto const& intType = dynamic_cast<IntegerType const&>(*_op.annotation().commonType);
smt::Expression left(expr(_op.leftExpression()));
smt::Expression right(expr(_op.rightExpression()));
- Token::Value op = _op.getOperator();
+ Token op = _op.getOperator();
smt::Expression value(
op == Token::Add ? left + right :
op == Token::Sub ? left - right :
@@ -644,7 +644,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op)
{
smt::Expression left(expr(_op.leftExpression()));
smt::Expression right(expr(_op.rightExpression()));
- Token::Value op = _op.getOperator();
+ Token op = _op.getOperator();
shared_ptr<smt::Expression> value;
if (isNumber(_op.annotation().commonType->category()))
{
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index ca9a9b57..f99b9ea4 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -112,17 +112,17 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Pragma);
vector<string> literals;
- vector<Token::Value> tokens;
+ vector<Token> tokens;
do
{
- Token::Value token = m_scanner->currentToken();
+ Token token = m_scanner->currentToken();
if (token == Token::Illegal)
parserError("Token incompatible with Solidity parser as part of pragma directive.");
else
{
string literal = m_scanner->currentLiteral();
- if (literal.empty() && Token::toString(token))
- literal = Token::toString(token);
+ if (literal.empty() && TokenTraits::toString(token))
+ literal = TokenTraits::toString(token);
literals.push_back(literal);
tokens.push_back(token);
}
@@ -240,7 +240,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
expectToken(Token::LBrace);
while (true)
{
- Token::Value currentTokenValue = m_scanner->currentToken();
+ Token currentTokenValue = m_scanner->currentToken();
if (currentTokenValue == Token::RBrace)
break;
else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor)
@@ -254,7 +254,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
else if (
currentTokenValue == Token::Identifier ||
currentTokenValue == Token::Mapping ||
- Token::isElementaryTypeName(currentTokenValue)
+ TokenTraits::isElementaryTypeName(currentTokenValue)
)
{
VarDeclParserOptions options;
@@ -304,7 +304,7 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
Declaration::Visibility Parser::parseVisibilitySpecifier()
{
Declaration::Visibility visibility(Declaration::Visibility::Default);
- Token::Value token = m_scanner->currentToken();
+ Token token = m_scanner->currentToken();
switch (token)
{
case Token::Public:
@@ -329,7 +329,7 @@ Declaration::Visibility Parser::parseVisibilitySpecifier()
StateMutability Parser::parseStateMutability()
{
StateMutability stateMutability(StateMutability::NonPayable);
- Token::Value token = m_scanner->currentToken();
+ Token token = m_scanner->currentToken();
switch(token)
{
case Token::Payable:
@@ -386,7 +386,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
result.parameters = parseParameterList(options);
while (true)
{
- Token::Value token = m_scanner->currentToken();
+ Token token = m_scanner->currentToken();
if (_allowModifiers && token == Token::Identifier)
{
// If the name is empty (and this is not a constructor),
@@ -401,7 +401,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
else
result.modifiers.push_back(parseModifierInvocation());
}
- else if (Token::isVisibilitySpecifier(token))
+ else if (TokenTraits::isVisibilitySpecifier(token))
{
if (result.visibility != Declaration::Visibility::Default)
{
@@ -423,7 +423,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
else
result.visibility = parseVisibilitySpecifier();
}
- else if (Token::isStateMutabilitySpecifier(token))
+ else if (TokenTraits::isStateMutabilitySpecifier(token))
{
if (result.stateMutability != StateMutability::NonPayable)
{
@@ -587,8 +587,8 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
while (true)
{
- Token::Value token = m_scanner->currentToken();
- if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
+ Token token = m_scanner->currentToken();
+ if (_options.isStateVariable && TokenTraits::isVariableVisibilitySpecifier(token))
{
nodeFactory.markEndPosition();
if (visibility != Declaration::Visibility::Default)
@@ -609,7 +609,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
isIndexed = true;
else if (token == Token::Constant)
isDeclaredConst = true;
- else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
+ else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Unspecified)
parserError(string("Location already specified."));
@@ -806,8 +806,8 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type;
- Token::Value token = m_scanner->currentToken();
- if (Token::isElementaryTypeName(token))
+ Token token = m_scanner->currentToken();
+ if (TokenTraits::isElementaryTypeName(token))
{
unsigned firstSize;
unsigned secondSize;
@@ -817,7 +817,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
nodeFactory.markEndPosition();
m_scanner->next();
auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable);
- if (Token::isStateMutabilitySpecifier(m_scanner->currentToken(), false))
+ if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false))
{
if (elemTypeName.token() == Token::Address)
{
@@ -874,8 +874,8 @@ ASTPointer<Mapping> Parser::parseMapping()
expectToken(Token::Mapping);
expectToken(Token::LParen);
ASTPointer<ElementaryTypeName> keyType;
- Token::Value token = m_scanner->currentToken();
- if (!Token::isElementaryTypeName(token))
+ Token token = m_scanner->currentToken();
+ if (!TokenTraits::isElementaryTypeName(token))
fatalParserError(string("Expected elementary type name for mapping key type"));
unsigned firstSize;
unsigned secondSize;
@@ -1252,7 +1252,7 @@ pair<Parser::LookAheadInfo, Parser::IndexAccessedPath> Parser::tryParseIndexAcce
// VariableDeclarationStatement out of it.
IndexAccessedPath iap = parseIndexAccessedPath();
- if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken()))
+ if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken()))
return make_pair(LookAheadInfo::VariableDeclaration, move(iap));
else
return make_pair(LookAheadInfo::Expression, move(iap));
@@ -1342,16 +1342,16 @@ ASTPointer<Expression> Parser::parseExpression(
{
RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseBinaryExpression(4, _partiallyParsedExpression);
- if (Token::isAssignmentOp(m_scanner->currentToken()))
+ if (TokenTraits::isAssignmentOp(m_scanner->currentToken()))
{
- Token::Value assignmentOperator = m_scanner->currentToken();
+ Token assignmentOperator = m_scanner->currentToken();
m_scanner->next();
ASTPointer<Expression> rightHandSide = parseExpression();
ASTNodeFactory nodeFactory(*this, expression);
nodeFactory.setEndPositionFromNode(rightHandSide);
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
}
- else if (m_scanner->currentToken() == Token::Value::Conditional)
+ else if (m_scanner->currentToken() == Token::Conditional)
{
m_scanner->next();
ASTPointer<Expression> trueExpression = parseExpression();
@@ -1373,11 +1373,11 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
RecursionGuard recursionGuard(*this);
ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression);
ASTNodeFactory nodeFactory(*this, expression);
- int precedence = Token::precedence(m_scanner->currentToken());
+ int precedence = TokenTraits::precedence(m_scanner->currentToken());
for (; precedence >= _minPrecedence; --precedence)
- while (Token::precedence(m_scanner->currentToken()) == precedence)
+ while (TokenTraits::precedence(m_scanner->currentToken()) == precedence)
{
- Token::Value op = m_scanner->currentToken();
+ Token op = m_scanner->currentToken();
m_scanner->next();
ASTPointer<Expression> right = parseBinaryExpression(precedence + 1);
nodeFactory.setEndPositionFromNode(right);
@@ -1393,8 +1393,8 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _partiallyParsedExpression ?
ASTNodeFactory(*this, _partiallyParsedExpression) : ASTNodeFactory(*this);
- Token::Value token = m_scanner->currentToken();
- if (!_partiallyParsedExpression && (Token::isUnaryOp(token) || Token::isCountOp(token)))
+ Token token = m_scanner->currentToken();
+ if (!_partiallyParsedExpression && (TokenTraits::isUnaryOp(token) || TokenTraits::isCountOp(token)))
{
// prefix expression
m_scanner->next();
@@ -1407,7 +1407,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
// potential postfix expression
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_partiallyParsedExpression);
token = m_scanner->currentToken();
- if (!Token::isCountOp(token))
+ if (!TokenTraits::isCountOp(token))
return subExpression;
nodeFactory.markEndPosition();
m_scanner->next();
@@ -1482,7 +1482,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
{
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
- Token::Value token = m_scanner->currentToken();
+ Token token = m_scanner->currentToken();
ASTPointer<Expression> expression;
switch (token)
@@ -1493,7 +1493,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
case Token::Number:
- if (Token::isEtherSubdenomination(m_scanner->peekNextToken()))
+ if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken()))
{
ASTPointer<ASTString> literal = getLiteralAndAdvance();
nodeFactory.markEndPosition();
@@ -1501,7 +1501,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next();
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
}
- else if (Token::isTimeSubdenomination(m_scanner->peekNextToken()))
+ else if (TokenTraits::isTimeSubdenomination(m_scanner->peekNextToken()))
{
ASTPointer<ASTString> literal = getLiteralAndAdvance();
nodeFactory.markEndPosition();
@@ -1531,7 +1531,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
// (x,) is one-dimensional tuple, elements in arrays cannot be left out, only in tuples.
m_scanner->next();
vector<ASTPointer<Expression>> components;
- Token::Value oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack);
+ Token oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack);
bool isArray = (token == Token::LBrack);
if (m_scanner->currentToken() != oppositeToken)
@@ -1558,7 +1558,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
fatalParserError("Expected even number of hex-nibbles within double-quotes.");
break;
default:
- if (Token::isElementaryTypeName(token))
+ if (TokenTraits::isElementaryTypeName(token))
{
//used for casts
unsigned firstSize;
@@ -1595,7 +1595,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
{
RecursionGuard recursionGuard(*this);
pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
- Token::Value token = m_scanner->currentToken();
+ Token token = m_scanner->currentToken();
if (token == Token::LBrace)
{
// call({arg1 : 1, arg2 : 2 })
@@ -1638,21 +1638,21 @@ Parser::LookAheadInfo Parser::peekStatementType() const
// or a mutability specifier, we also have a variable declaration.
// If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;").
// In all other cases, we have an expression statement.
- Token::Value token(m_scanner->currentToken());
- bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
+ Token token(m_scanner->currentToken());
+ bool mightBeTypeName = (TokenTraits::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Function || token == Token::Var)
return LookAheadInfo::VariableDeclaration;
if (mightBeTypeName)
{
- Token::Value next = m_scanner->peekNextToken();
+ Token next = m_scanner->peekNextToken();
// So far we only allow ``address payable`` in variable declaration statements and in no other
// kind of statement. This means, for example, that we do not allow type expressions of the form
// ``address payable;``.
// If we want to change this in the future, we need to consider another scanner token here.
- if (Token::isElementaryTypeName(token) && Token::isStateMutabilitySpecifier(next, false))
+ if (TokenTraits::isElementaryTypeName(token) && TokenTraits::isStateMutabilitySpecifier(next, false))
return LookAheadInfo::VariableDeclaration;
- if (next == Token::Identifier || Token::isLocationSpecifier(next))
+ if (next == Token::Identifier || TokenTraits::isLocationSpecifier(next))
return LookAheadInfo::VariableDeclaration;
if (next == Token::LBrack || next == Token::Period)
return LookAheadInfo::IndexAccessStructure;
diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp
index 71133746..1d4cb1e2 100644
--- a/libsolidity/parsing/ParserBase.cpp
+++ b/libsolidity/parsing/ParserBase.cpp
@@ -43,12 +43,12 @@ int ParserBase::endPosition() const
return m_scanner->currentLocation().end;
}
-Token::Value ParserBase::currentToken() const
+Token ParserBase::currentToken() const
{
return m_scanner->currentToken();
}
-Token::Value ParserBase::peekNextToken() const
+Token ParserBase::peekNextToken() const
{
return m_scanner->peekNextToken();
}
@@ -58,31 +58,31 @@ std::string ParserBase::currentLiteral() const
return m_scanner->currentLiteral();
}
-Token::Value ParserBase::advance()
+Token ParserBase::advance()
{
return m_scanner->next();
}
-void ParserBase::expectToken(Token::Value _value, bool _advance)
+void ParserBase::expectToken(Token _value, bool _advance)
{
- Token::Value tok = m_scanner->currentToken();
+ Token tok = m_scanner->currentToken();
if (tok != _value)
{
- auto tokenName = [this](Token::Value _token)
+ auto tokenName = [this](Token _token)
{
if (_token == Token::Identifier)
return string("identifier");
else if (_token == Token::EOS)
return string("end of source");
- else if (Token::isReservedKeyword(_token))
- return string("reserved keyword '") + Token::friendlyName(_token) + "'";
- else if (Token::isElementaryTypeName(_token)) //for the sake of accuracy in reporting
+ else if (TokenTraits::isReservedKeyword(_token))
+ return string("reserved keyword '") + TokenTraits::friendlyName(_token) + "'";
+ else if (TokenTraits::isElementaryTypeName(_token)) //for the sake of accuracy in reporting
{
ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
return string("'") + elemTypeName.toString() + "'";
}
else
- return string("'") + Token::friendlyName(_token) + "'";
+ return string("'") + TokenTraits::friendlyName(_token) + "'";
};
fatalParserError(string("Expected ") + tokenName(_value) + string(" but got ") + tokenName(tok));
diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h
index b28e1b1b..e01f37d8 100644
--- a/libsolidity/parsing/ParserBase.h
+++ b/libsolidity/parsing/ParserBase.h
@@ -63,11 +63,11 @@ protected:
///@{
///@name Helper functions
/// If current token value is not _value, throw exception otherwise advance token.
- void expectToken(Token::Value _value, bool _advance = true);
- Token::Value currentToken() const;
- Token::Value peekNextToken() const;
+ void expectToken(Token _value, bool _advance = true);
+ Token currentToken() const;
+ Token peekNextToken() const;
std::string currentLiteral() const;
- Token::Value advance();
+ Token advance();
///@}
/// Increases the recursion depth and throws an exception if it is too deep.
diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp
index 87d7c535..e9dad2ad 100644
--- a/libsolidity/parsing/Scanner.cpp
+++ b/libsolidity/parsing/Scanner.cpp
@@ -214,9 +214,9 @@ void Scanner::addUnicodeAsUTF8(unsigned codepoint)
}
// Ensure that tokens can be stored in a byte.
-BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
+BOOST_STATIC_ASSERT(TokenTraits::count() <= 0x100);
-Token::Value Scanner::next()
+Token Scanner::next()
{
m_currentToken = m_nextToken;
m_skippedComment = m_nextSkippedComment;
@@ -225,7 +225,7 @@ Token::Value Scanner::next()
return m_currentToken.token;
}
-Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else)
+Token Scanner::selectToken(char _next, Token _then, Token _else)
{
advance();
if (m_char == _next)
@@ -249,7 +249,7 @@ void Scanner::skipWhitespaceExceptUnicodeLinebreak()
advance();
}
-Token::Value Scanner::skipSingleLineComment()
+Token Scanner::skipSingleLineComment()
{
// Line terminator is not part of the comment. If it is a
// non-ascii line terminator, it will result in a parser error.
@@ -259,7 +259,7 @@ Token::Value Scanner::skipSingleLineComment()
return Token::Whitespace;
}
-Token::Value Scanner::scanSingleLineDocComment()
+Token Scanner::scanSingleLineDocComment()
{
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
advance(); //consume the last '/' at ///
@@ -295,7 +295,7 @@ Token::Value Scanner::scanSingleLineDocComment()
return Token::CommentLiteral;
}
-Token::Value Scanner::skipMultiLineComment()
+Token Scanner::skipMultiLineComment()
{
advance();
while (!isSourcePastEndOfInput())
@@ -316,7 +316,7 @@ Token::Value Scanner::skipMultiLineComment()
return Token::Illegal;
}
-Token::Value Scanner::scanMultiLineDocComment()
+Token Scanner::scanMultiLineDocComment()
{
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
bool endFound = false;
@@ -369,7 +369,7 @@ Token::Value Scanner::scanMultiLineDocComment()
return Token::CommentLiteral;
}
-Token::Value Scanner::scanSlash()
+Token Scanner::scanSlash()
{
int firstSlashPosition = sourcePos();
advance();
@@ -380,7 +380,7 @@ Token::Value Scanner::scanSlash()
else if (m_char == '/')
{
// doxygen style /// comment
- Token::Value comment;
+ Token comment;
m_nextSkippedComment.location.start = firstSlashPosition;
comment = scanSingleLineDocComment();
m_nextSkippedComment.location.end = sourcePos();
@@ -406,7 +406,7 @@ Token::Value Scanner::scanSlash()
return Token::Whitespace;
}
// we actually have a multiline documentation comment
- Token::Value comment;
+ Token comment;
m_nextSkippedComment.location.start = firstSlashPosition;
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = sourcePos();
@@ -432,7 +432,7 @@ void Scanner::scanToken()
m_nextSkippedComment.literal.clear();
m_nextSkippedComment.extendedTokenInfo = make_tuple(0, 0);
- Token::Value token;
+ Token token;
// M and N are for the purposes of grabbing different type sizes
unsigned m;
unsigned n;
@@ -703,7 +703,7 @@ bool Scanner::isUnicodeLinebreak()
return false;
}
-Token::Value Scanner::scanString()
+Token Scanner::scanString()
{
char const quote = m_char;
advance(); // consume quote
@@ -727,7 +727,7 @@ Token::Value Scanner::scanString()
return Token::StringLiteral;
}
-Token::Value Scanner::scanHexString()
+Token Scanner::scanHexString()
{
char const quote = m_char;
advance(); // consume quote
@@ -760,7 +760,7 @@ void Scanner::scanDecimalDigits()
// Defer further validation of underscore to SyntaxChecker.
}
-Token::Value Scanner::scanNumber(char _charSeen)
+Token Scanner::scanNumber(char _charSeen)
{
enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
LiteralScope literal(this, LITERAL_TYPE_NUMBER);
@@ -854,7 +854,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
return Token::Number;
}
-tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword()
+tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword()
{
solAssert(isIdentifierStart(m_char), "");
LiteralScope literal(this, LITERAL_TYPE_STRING);
@@ -863,7 +863,7 @@ tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword()
while (isIdentifierPart(m_char)) //get full literal
addLiteralCharAndAdvance();
literal.complete();
- return Token::fromIdentifierOrKeyword(m_nextToken.literal);
+ return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal);
}
char CharStream::advanceAndGet(size_t _chars)
diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h
index 7564c788..02e0553f 100644
--- a/libsolidity/parsing/Scanner.h
+++ b/libsolidity/parsing/Scanner.h
@@ -112,13 +112,13 @@ public:
void reset();
/// @returns the next token and advances input
- Token::Value next();
+ Token next();
///@{
///@name Information about the current token
/// @returns the current token
- Token::Value currentToken() const
+ Token currentToken() const
{
return m_currentToken.token;
}
@@ -149,7 +149,7 @@ public:
///@name Information about the next token
/// @returns the next token without advancing input.
- Token::Value peekNextToken() const { return m_nextToken.token; }
+ Token peekNextToken() const { return m_nextToken.token; }
SourceLocation peekLocation() const { return m_nextToken.location; }
std::string const& peekLiteral() const { return m_nextToken.literal; }
///@}
@@ -168,7 +168,7 @@ private:
/// Used for the current and look-ahead token and comments
struct TokenDesc
{
- Token::Value token;
+ Token token;
SourceLocation location;
std::string literal;
std::tuple<unsigned, unsigned> extendedTokenInfo;
@@ -185,9 +185,9 @@ private:
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int _amount) { m_char = m_source.rollback(_amount); }
- inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; }
+ inline Token selectToken(Token _tok) { advance(); return _tok; }
/// If the next character is _next, advance and return _then, otherwise return _else.
- inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else);
+ inline Token selectToken(char _next, Token _then, Token _else);
bool scanHexByte(char& o_scannedByte);
bool scanUnicode(unsigned& o_codepoint);
@@ -199,19 +199,19 @@ private:
bool skipWhitespace();
/// Skips all whitespace that are neither '\r' nor '\n'.
void skipWhitespaceExceptUnicodeLinebreak();
- Token::Value skipSingleLineComment();
- Token::Value skipMultiLineComment();
+ Token skipSingleLineComment();
+ Token skipMultiLineComment();
void scanDecimalDigits();
- Token::Value scanNumber(char _charSeen = 0);
- std::tuple<Token::Value, unsigned, unsigned> scanIdentifierOrKeyword();
+ Token scanNumber(char _charSeen = 0);
+ std::tuple<Token, unsigned, unsigned> scanIdentifierOrKeyword();
- Token::Value scanString();
- Token::Value scanHexString();
- Token::Value scanSingleLineDocComment();
- Token::Value scanMultiLineDocComment();
+ Token scanString();
+ Token scanHexString();
+ Token scanSingleLineDocComment();
+ Token scanMultiLineDocComment();
/// Scans a slash '/' and depending on the characters returns the appropriate token
- Token::Value scanSlash();
+ Token scanSlash();
/// Scans an escape-sequence which is part of a string and adds the
/// decoded character to the current literal. Returns true if a pattern
diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp
index 27acb7d4..dccd9037 100644
--- a/libsolidity/parsing/Token.cpp
+++ b/libsolidity/parsing/Token.cpp
@@ -51,9 +51,9 @@ namespace dev
namespace solidity
{
-void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second)
+void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second)
{
- solAssert(Token::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(Token::toString(_baseType)));
+ solAssert(TokenTraits::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(TokenTraits::toString(_baseType)));
if (_baseType == Token::BytesM)
{
solAssert(_second == 0, "There should not be a second size argument to type bytesM.");
@@ -61,17 +61,17 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con
}
else if (_baseType == Token::UIntM || _baseType == Token::IntM)
{
- solAssert(_second == 0, "There should not be a second size argument to type " + string(Token::toString(_baseType)) + ".");
+ solAssert(_second == 0, "There should not be a second size argument to type " + string(TokenTraits::toString(_baseType)) + ".");
solAssert(
_first <= 256 && _first % 8 == 0,
- "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "."
+ "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "."
);
}
else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN)
{
solAssert(
_first >= 8 && _first <= 256 && _first % 8 == 0 && _second <= 80,
- "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "."
+ "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "."
);
}
m_token = _baseType;
@@ -79,38 +79,54 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con
m_secondNumber = _second;
}
-#define T(name, string, precedence) #name,
-char const* const Token::m_name[NUM_TOKENS] =
+namespace TokenTraits
{
- TOKEN_LIST(T, T)
-};
-#undef T
+char const* toString(Token tok)
+{
+ switch (tok)
+ {
+#define T(name, string, precedence) case Token::name: return string;
+ TOKEN_LIST(T, T)
+#undef T
+ default: // Token::NUM_TOKENS:
+ return "";
+ }
+}
-#define T(name, string, precedence) string,
-char const* const Token::m_string[NUM_TOKENS] =
+char const* name(Token tok)
{
- TOKEN_LIST(T, T)
-};
+#define T(name, string, precedence) #name,
+ static char const* const names[TokenTraits::count()] = { TOKEN_LIST(T, T) };
#undef T
+ solAssert(static_cast<size_t>(tok) < TokenTraits::count(), "");
+ return names[static_cast<size_t>(tok)];
+}
-#define T(name, string, precedence) precedence,
-int8_t const Token::m_precedence[NUM_TOKENS] =
+std::string friendlyName(Token tok)
{
- TOKEN_LIST(T, T)
-};
-#undef T
+ char const* ret = toString(tok);
+ if (ret)
+ return std::string(ret);
+ ret = name(tok);
+ solAssert(ret != nullptr, "");
+ return std::string(ret);
+}
-#define KT(a, b, c) 'T',
-#define KK(a, b, c) 'K',
-char const Token::m_tokenType[] =
+#define T(name, string, precedence) precedence,
+int precedence(Token tok)
{
- TOKEN_LIST(KT, KK)
-};
+ int8_t const static precs[TokenTraits::count()] =
+ {
+ TOKEN_LIST(T, T)
+ };
+ return precs[static_cast<size_t>(tok)];
+}
+#undef T
-int Token::parseSize(string::const_iterator _begin, string::const_iterator _end)
+int parseSize(string::const_iterator _begin, string::const_iterator _end)
{
try
{
@@ -123,7 +139,20 @@ int Token::parseSize(string::const_iterator _begin, string::const_iterator _end)
}
}
-tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(string const& _literal)
+static Token keywordByName(string const& _name)
+{
+ // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
+ // and keywords to be put inside the keywords variable.
+#define KEYWORD(name, string, precedence) {string, Token::name},
+#define TOKEN(name, string, precedence)
+ static const map<string, Token> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
+#undef KEYWORD
+#undef TOKEN
+ auto it = keywords.find(_name);
+ return it == keywords.end() ? Token::Identifier : it->second;
+}
+
+tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(string const& _literal)
{
auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit);
if (positionM != _literal.end())
@@ -131,7 +160,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s
string baseType(_literal.begin(), positionM);
auto positionX = find_if_not(positionM, _literal.end(), ::isdigit);
int m = parseSize(positionM, positionX);
- Token::Value keyword = keywordByName(baseType);
+ Token keyword = keywordByName(baseType);
if (keyword == Token::Bytes)
{
if (0 < m && m <= 32 && positionX == _literal.end())
@@ -172,20 +201,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s
return make_tuple(keywordByName(_literal), 0, 0);
}
-Token::Value Token::keywordByName(string const& _name)
-{
- // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
- // and keywords to be put inside the keywords variable.
-#define KEYWORD(name, string, precedence) {string, Token::name},
-#define TOKEN(name, string, precedence)
- static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
-#undef KEYWORD
-#undef TOKEN
- auto it = keywords.find(_name);
- return it == keywords.end() ? Token::Identifier : it->second;
-}
-#undef KT
-#undef KK
+}
}
}
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 8ecc850a..81e8dd98 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -45,6 +45,7 @@
#include <libdevcore/Common.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/parsing/UndefMacros.h>
+#include <iosfwd>
namespace dev
{
@@ -267,119 +268,95 @@ namespace solidity
/* Scanner-internal use only. */ \
T(Whitespace, NULL, 0)
-
-class Token
-{
-public:
- // All token values.
- // attention! msvc issue:
- // http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059
- // @todo: avoid TOKEN_LIST macro
+// All token values.
+// attention! msvc issue:
+// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059
+// @todo: avoid TOKEN_LIST macro
+enum class Token : unsigned int {
#define T(name, string, precedence) name,
- enum Value
- {
- TOKEN_LIST(T, T)
- NUM_TOKENS
- };
+ TOKEN_LIST(T, T)
+ NUM_TOKENS
#undef T
+};
- // @returns a string corresponding to the C++ token name
- // (e.g. "LT" for the token LT).
- static char const* name(Value tok)
- {
- solAssert(tok < NUM_TOKENS, "");
- return m_name[tok];
- }
+namespace TokenTraits
+{
+ constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); }
// Predicates
- static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; }
- static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; }
- static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; }
- static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd ||
- op == Add || op == Mul || op == Equal || op == NotEqual; }
- static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; }
- static bool isCompareOp(Value op) { return Equal <= op && op <= GreaterThanOrEqual; }
+ constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; }
+ constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
+ constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }
+ constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd ||
+ op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; }
+ constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; }
+ constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; }
- static Value AssignmentToBinaryOp(Value op)
- {
- solAssert(isAssignmentOp(op) && op != Assign, "");
- return Value(op + (BitOr - AssignBitOr));
- }
+ constexpr bool isBitOp(Token op) { return (Token::BitOr <= op && op <= Token::BitAnd) || op == Token::BitNot; }
+ constexpr bool isBooleanOp(Token op) { return (Token::Or <= op && op <= Token::And) || op == Token::Not; }
+ constexpr bool isUnaryOp(Token op) { return (Token::Not <= op && op <= Token::Delete) || op == Token::Add || op == Token::Sub; }
+ constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; }
+ constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); }
+ constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; }
+ constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; }
+ constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; }
- static bool isBitOp(Value op) { return (BitOr <= op && op <= BitAnd) || op == BitNot; }
- static bool isBooleanOp(Value op) { return (Or <= op && op <= And) || op == Not; }
- static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; }
- static bool isCountOp(Value op) { return op == Inc || op == Dec; }
- static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
- static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
- static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
- static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage || op == CallData; }
- static bool isStateMutabilitySpecifier(Value op, bool _allowConstant = true)
+ constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true)
{
- if (op == Constant && _allowConstant)
- return true;
- return op == Pure || op == View || op == Payable;
+ return (op == Token::Constant && _allowConstant)
+ || op == Token::Pure || op == Token::View || op == Token::Payable;
}
- static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
- static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
- static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= Unchecked); }
- // @returns a string corresponding to the JS token string
- // (.e., "<" for the token LT) or NULL if the token doesn't
- // have a (unique) string (e.g. an IDENTIFIER).
- static char const* toString(Value tok)
- {
- solAssert(tok < NUM_TOKENS, "");
- return m_string[tok];
- }
+ constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; }
+ constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; }
+ constexpr bool isReservedKeyword(Token op) { return (Token::Abstract <= op && op <= Token::Unchecked); }
- static std::string friendlyName(Value tok)
+ inline Token AssignmentToBinaryOp(Token op)
{
- char const* ret = toString(tok);
- if (ret == nullptr)
- {
- ret = name(tok);
- solAssert(ret != nullptr, "");
- }
- return std::string(ret);
+ solAssert(isAssignmentOp(op) && op != Token::Assign, "");
+ return static_cast<Token>(static_cast<int>(op) + (static_cast<int>(Token::BitOr) - static_cast<int>(Token::AssignBitOr)));
}
// @returns the precedence > 0 for binary and compare
// operators; returns 0 otherwise.
- static int precedence(Value tok)
- {
- solAssert(tok < NUM_TOKENS, "");
- return m_precedence[tok];
- }
+ int precedence(Token tok);
- static std::tuple<Token::Value, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal);
+ std::tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal);
-private:
- // @returns -1 on error (invalid digit or number too large)
- static int parseSize(std::string::const_iterator _begin, std::string::const_iterator _end);
- // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists.
- static Token::Value keywordByName(std::string const& _name);
- static char const* const m_name[NUM_TOKENS];
- static char const* const m_string[NUM_TOKENS];
- static int8_t const m_precedence[NUM_TOKENS];
- static char const m_tokenType[NUM_TOKENS];
-};
+ // @returns a string corresponding to the C++ token name
+ // (e.g. "LT" for the token LT).
+ char const* name(Token tok);
+
+ // @returns a string corresponding to the JS token string
+ // (.e., "<" for the token LT) or NULL if the token doesn't
+ // have a (unique) string (e.g. an IDENTIFIER).
+ char const* toString(Token tok);
+
+ std::string friendlyName(Token tok);
+}
+
+inline std::ostream& operator<<(std::ostream& os, Token token)
+{
+ os << TokenTraits::friendlyName(token);
+ return os;
+}
class ElementaryTypeNameToken
{
public:
- ElementaryTypeNameToken(Token::Value _token, unsigned const& _firstNumber, unsigned const& _secondNumber)
+ ElementaryTypeNameToken(Token _token, unsigned const& _firstNumber, unsigned const& _secondNumber)
{
assertDetails(_token, _firstNumber, _secondNumber);
}
unsigned int firstNumber() const { return m_firstNumber; }
unsigned int secondNumber() const { return m_secondNumber; }
- Token::Value token() const { return m_token; }
+ Token token() const { return m_token; }
+
///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type
std::string toString(bool const& tokenValue = false) const
{
- std::string name = Token::toString(m_token);
+ std::string name = TokenTraits::toString(m_token);
if (tokenValue || (firstNumber() == 0 && secondNumber() == 0))
return name;
solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here.");
@@ -390,11 +367,11 @@ public:
}
private:
- Token::Value m_token;
+ Token m_token;
unsigned int m_firstNumber;
unsigned int m_secondNumber;
/// throws if type is not properly sized
- void assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second);
+ void assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second);
};
}
diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp
new file mode 100644
index 00000000..478858e4
--- /dev/null
+++ b/libyul/optimiser/RedundantAssignEliminator.cpp
@@ -0,0 +1,193 @@
+/*
+ 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/>.
+*/
+/**
+ * Optimiser component that removes assignments to variables that are not used
+ * until they go out of scope or are re-assigned.
+ */
+
+#include <libyul/optimiser/RedundantAssignEliminator.h>
+
+#include <libyul/optimiser/Semantics.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+#include <libdevcore/CommonData.h>
+
+#include <boost/range/algorithm_ext/erase.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace dev::yul;
+using namespace dev::solidity;
+
+void RedundantAssignEliminator::operator()(Identifier const& _identifier)
+{
+ changeUndecidedTo(_identifier.name, State::Used);
+}
+
+void RedundantAssignEliminator::operator()(VariableDeclaration const& _variableDeclaration)
+{
+ ASTWalker::operator()(_variableDeclaration);
+
+ for (auto const& var: _variableDeclaration.variables)
+ m_declaredVariables.insert(var.name);
+}
+
+void RedundantAssignEliminator::operator()(Assignment const& _assignment)
+{
+ visit(*_assignment.value);
+ for (auto const& var: _assignment.variableNames)
+ changeUndecidedTo(var.name, State::Unused);
+
+ if (_assignment.variableNames.size() == 1)
+ // Default-construct it in "Undecided" state if it does not yet exist.
+ m_assignments[_assignment.variableNames.front().name][&_assignment];
+}
+
+void RedundantAssignEliminator::operator()(If const& _if)
+{
+ visit(*_if.condition);
+
+ RedundantAssignEliminator branch{*this};
+ branch(_if.body);
+
+ join(branch);
+}
+
+void RedundantAssignEliminator::operator()(Switch const& _switch)
+{
+ visit(*_switch.expression);
+
+ bool hasDefault = false;
+ vector<RedundantAssignEliminator> branches;
+ for (auto const& c: _switch.cases)
+ {
+ if (!c.value)
+ hasDefault = true;
+ branches.emplace_back(*this);
+ branches.back()(c.body);
+ }
+
+ if (hasDefault)
+ {
+ *this = std::move(branches.back());
+ branches.pop_back();
+ }
+ for (auto& branch: branches)
+ join(branch);
+}
+
+void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition)
+{
+ (*this)(_functionDefinition.body);
+
+ for (auto const& param: _functionDefinition.parameters)
+ changeUndecidedTo(param.name, State::Unused);
+ for (auto const& retParam: _functionDefinition.returnVariables)
+ changeUndecidedTo(retParam.name, State::Used);
+}
+
+void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
+{
+ // This will set all variables that are declared in this
+ // block to "unused" when it is destroyed.
+ BlockScope scope(*this);
+
+ // We need to visit the statements directly because of the
+ // scoping rules.
+ walkVector(_forLoop.pre.statements);
+
+ // We just run the loop twice to account for the
+ // back edge.
+ // There need not be more runs because we only have three different states.
+
+ visit(*_forLoop.condition);
+
+ RedundantAssignEliminator zeroRuns{*this};
+
+ (*this)(_forLoop.body);
+ (*this)(_forLoop.post);
+
+ visit(*_forLoop.condition);
+
+ RedundantAssignEliminator oneRun{*this};
+
+ (*this)(_forLoop.body);
+ (*this)(_forLoop.post);
+
+ visit(*_forLoop.condition);
+
+ // Order does not matter because "max" is commutative and associative.
+ join(oneRun);
+ join(zeroRuns);
+}
+
+void RedundantAssignEliminator::operator()(Block const& _block)
+{
+ // This will set all variables that are declared in this
+ // block to "unused" when it is destroyed.
+ BlockScope scope(*this);
+
+ ASTWalker::operator()(_block);
+}
+
+void RedundantAssignEliminator::run(Block& _ast)
+{
+ RedundantAssignEliminator rae;
+ rae(_ast);
+
+ std::set<Assignment const*> assignmentsToRemove;
+ for (auto const& variables: rae.m_assignments)
+ for (auto const& assignment: variables.second)
+ {
+ assertThrow(assignment.second != State::Undecided, OptimizerException, "");
+ if (assignment.second == State::Unused && MovableChecker{*assignment.first->value}.movable())
+ assignmentsToRemove.insert(assignment.first);
+ }
+
+ AssignmentRemover remover{assignmentsToRemove};
+ remover(_ast);
+}
+
+void RedundantAssignEliminator::join(RedundantAssignEliminator& _other)
+{
+ for (auto& var: _other.m_assignments)
+ if (m_assignments.count(var.first))
+ {
+ map<Assignment const*, State>& assignmentsHere = m_assignments[var.first];
+ for (auto& assignment: var.second)
+ assignmentsHere[assignment.first].join(assignment.second);
+ }
+ else
+ m_assignments[var.first] = std::move(var.second);
+}
+
+void RedundantAssignEliminator::changeUndecidedTo(string const& _variable, RedundantAssignEliminator::State _newState)
+{
+ for (auto& assignment: m_assignments[_variable])
+ if (assignment.second == State{State::Undecided})
+ assignment.second = _newState;
+}
+
+void AssignmentRemover::operator()(Block& _block)
+{
+ boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool {
+ return _statement.type() == typeid(Assignment) && m_toRemove.count(&boost::get<Assignment>(_statement));
+ });
+
+ ASTModifier::operator()(_block);
+}
diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h
new file mode 100644
index 00000000..52092138
--- /dev/null
+++ b/libyul/optimiser/RedundantAssignEliminator.h
@@ -0,0 +1,185 @@
+/*
+ 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/>.
+*/
+/**
+ * Optimiser component that removes assignments to variables that are not used
+ * until they go out of scope or are re-assigned.
+ */
+
+#pragma once
+
+#include <libyul/ASTDataForward.h>
+
+#include <libyul/optimiser/ASTWalker.h>
+
+#include <map>
+
+namespace dev
+{
+namespace yul
+{
+
+/**
+ * Optimiser component that removes assignments to variables that are not used
+ * until they go out of scope or are re-assigned. This component
+ * respects the control-flow and takes it into account for removal.
+ *
+ * Example:
+ *
+ * {
+ * let a
+ * a := 1
+ * a := 2
+ * b := 2
+ * if calldataload(0)
+ * {
+ * b := mload(a)
+ * }
+ * a := b
+ * }
+ *
+ * In the example, "a := 1" can be removed because the value from this assignment
+ * is not used in any control-flow branch (it is replaced right away).
+ * The assignment "a := 2" is also overwritten by "a := b" at the end,
+ * but there is a control-flow path (through the condition body) which uses
+ * the value from "a := 2" and thus, this assignment cannot be removed.
+ *
+ * Detailed rules:
+ *
+ * The AST is traversed twice: in an information gathering step and in the
+ * actual removal step. During information gathering, we maintain a
+ * mapping from assignment statements to the three states
+ * "unused", "undecided" and "used".
+ * When an assignment is visited, it is added to the mapping in the "undecided" state
+ * (see remark about for loops below) and every other assignment to the same variable
+ * that is still in the "undecided" state is changed to "unused".
+ * When a variable is referenced, the state of any assignment to that variable still
+ * in the "undecided" state is changed to "used".
+ * At points where control flow splits, a copy
+ * of the mapping is handed over to each branch. At points where control flow
+ * joins, the two mappings coming from the two branches are combined in the following way:
+ * Statements that are only in one mapping or have the same state are used unchanged.
+ * Conflicting values are resolved in the following way:
+ * "unused", "undecided" -> "undecided"
+ * "unused", "used" -> "used"
+ * "undecided, "used" -> "used".
+ *
+ * For for-loops, the condition, body and post-part are visited twice, taking
+ * the joining control-flow at the condition into account.
+ * In other words, we create three control flow paths: Zero runs of the loop,
+ * one run and two runs and then combine them at the end.
+ * Running at most twice is enough because there are only three different states.
+ *
+ * For switch statements that have a "default"-case, there is no control-flow
+ * part that skips the switch.
+ *
+ * When a variable goes out of scope, all statements still in the "undecided"
+ * state are changed to "unused", unless the variable is the return
+ * parameter of a function - there, the state changes to "used".
+ *
+ * In the second traversal, all assignments that are in the "unused" state are removed.
+ *
+ *
+ * This step is usually run right after the SSA transform to complete
+ * the generation of the pseudo-SSA.
+ *
+ * Prerequisite: Disambiguator.
+ */
+class RedundantAssignEliminator: public ASTWalker
+{
+public:
+ RedundantAssignEliminator(RedundantAssignEliminator const&) = default;
+ RedundantAssignEliminator& operator=(RedundantAssignEliminator const&) = default;
+ RedundantAssignEliminator(RedundantAssignEliminator&&) = default;
+ RedundantAssignEliminator& operator=(RedundantAssignEliminator&&) = default;
+
+ void operator()(Identifier const& _identifier) override;
+ void operator()(VariableDeclaration const& _variableDeclaration) override;
+ void operator()(Assignment const& _assignment) override;
+ void operator()(If const& _if) override;
+ void operator()(Switch const& _switch) override;
+ void operator()(FunctionDefinition const&) override;
+ void operator()(ForLoop const&) override;
+ void operator()(Block const& _block) override;
+
+ static void run(Block& _ast);
+
+private:
+ RedundantAssignEliminator() {}
+
+ class State
+ {
+ public:
+ enum Value { Unused, Undecided, Used };
+ State(Value _value = Undecided): m_value(_value) {}
+ bool operator==(State _other) const { return m_value == _other.m_value; }
+ bool operator!=(State _other) const { return !operator==(_other); }
+ void join(State _other)
+ {
+ // Using "max" works here because of the order of the values in the enum.
+ m_value = Value(std::max(int(_other.m_value), int(m_value)));
+ }
+ private:
+ Value m_value = Undecided;
+ };
+
+ /**
+ * Takes care about storing the list of declared variables and
+ * sets them to "unused" when it is destroyed.
+ */
+ class BlockScope
+ {
+ public:
+ explicit BlockScope(RedundantAssignEliminator& _rae): m_rae(_rae)
+ {
+ swap(m_rae.m_declaredVariables, m_outerDeclaredVariables);
+ }
+ ~BlockScope()
+ {
+ for (auto const& var: m_rae.m_declaredVariables)
+ m_rae.changeUndecidedTo(var, State::Unused);
+ swap(m_rae.m_declaredVariables, m_outerDeclaredVariables);
+ }
+
+ private:
+ RedundantAssignEliminator& m_rae;
+ std::set<std::string> m_outerDeclaredVariables;
+ };
+
+ /// Joins the assignment mapping with @a _other according to the rules laid out
+ /// above.
+ /// Will destroy @a _other.
+ void join(RedundantAssignEliminator& _other);
+ void changeUndecidedTo(std::string const& _variable, State _newState);
+
+ std::set<std::string> m_declaredVariables;
+ std::map<std::string, std::map<Assignment const*, State>> m_assignments;
+};
+
+class AssignmentRemover: public ASTModifier
+{
+public:
+ explicit AssignmentRemover(std::set<Assignment const*> const& _toRemove):
+ m_toRemove(_toRemove)
+ {}
+ void operator()(Block& _block) override;
+
+private:
+ std::set<Assignment const*> const& m_toRemove;
+};
+
+}
+}
diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp
index 07f8fba6..43951f73 100644
--- a/test/libsolidity/SemVerMatcher.cpp
+++ b/test/libsolidity/SemVerMatcher.cpp
@@ -42,13 +42,13 @@ SemVerMatchExpression parseExpression(string const& _input)
{
Scanner scanner{CharStream(_input)};
vector<string> literals;
- vector<Token::Value> tokens;
+ vector<Token> tokens;
while (scanner.currentToken() != Token::EOS)
{
auto token = scanner.currentToken();
string literal = scanner.currentLiteral();
- if (literal.empty() && Token::toString(token))
- literal = Token::toString(token);
+ if (literal.empty() && TokenTraits::toString(token))
+ literal = TokenTraits::toString(token);
literals.push_back(literal);
tokens.push_back(token);
scanner.next();
diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp
index 6782f412..67715ac1 100644
--- a/test/libyul/YulOptimizerTest.cpp
+++ b/test/libyul/YulOptimizerTest.cpp
@@ -36,6 +36,7 @@
#include <libyul/optimiser/UnusedPruner.h>
#include <libyul/optimiser/ExpressionJoiner.h>
#include <libyul/optimiser/SSATransform.h>
+#include <libyul/optimiser/RedundantAssignEliminator.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
@@ -178,6 +179,18 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con
NameDispenser nameDispenser(*m_ast);
SSATransform::run(*m_ast, nameDispenser);
}
+ else if (m_optimizerStep == "redundantAssignEliminator")
+ {
+ disambiguate();
+ RedundantAssignEliminator::run(*m_ast);
+ }
+ else if (m_optimizerStep == "ssaPlusCleanup")
+ {
+ disambiguate();
+ NameDispenser nameDispenser(*m_ast);
+ SSATransform::run(*m_ast, nameDispenser);
+ RedundantAssignEliminator::run(*m_ast);
+ }
else
{
FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl;
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul
new file mode 100644
index 00000000..d9bbd86d
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul
@@ -0,0 +1,26 @@
+{
+ for {
+ let a := 2
+ // Should not be removed, even though you might think
+ // it goes out of scope
+ a := 3
+ } a { a := add(a, 1) }
+ {
+ a := 7
+ }
+}
+// ----
+// redundantAssignEliminator
+// {
+// for {
+// let a := 2
+// a := 3
+// }
+// a
+// {
+// a := add(a, 1)
+// }
+// {
+// a := 7
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul
new file mode 100644
index 00000000..7f5e97ce
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul
@@ -0,0 +1,31 @@
+{
+ let x
+ let y
+ // Cannot be removed, because we might skip the loop
+ x := 1
+ for { } calldataload(0) { }
+ {
+ // Cannot be removed
+ x := 2
+ // Can be removed
+ y := 3
+ }
+ y := 8
+ mstore(x, 0)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let x
+// let y
+// x := 1
+// for {
+// }
+// calldataload(0)
+// {
+// }
+// {
+// x := 2
+// }
+// mstore(x, 0)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul
new file mode 100644
index 00000000..65eb2838
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul
@@ -0,0 +1,27 @@
+{
+ let x
+ // Cannot be removed, because we might run the loop only once
+ x := 1
+ for { } calldataload(0) { }
+ {
+ mstore(x, 2)
+ // Cannot be removed because of the line above
+ x := 2
+ }
+ x := 3
+}
+// ----
+// redundantAssignEliminator
+// {
+// let x
+// x := 1
+// for {
+// }
+// calldataload(0)
+// {
+// }
+// {
+// mstore(x, 2)
+// x := 2
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul
new file mode 100644
index 00000000..5bb920ec
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul
@@ -0,0 +1,23 @@
+{
+ let r
+ r := 1
+ function f(x, y) -> a, b {
+ // Can be removed, is param
+ x := 1
+ y := 2
+ // Cannot be removed, is return param
+ a := 3
+ b := 4
+ }
+ r := 2
+}
+// ----
+// redundantAssignEliminator
+// {
+// let r
+// function f(x, y) -> a, b
+// {
+// a := 3
+// b := 4
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul
new file mode 100644
index 00000000..958bfc66
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul
@@ -0,0 +1,24 @@
+{
+ let c
+ let d
+ c := calldataload(0)
+ d := 1
+ if c {
+ d := 2
+ }
+ // This enforces that none of the assignments above can be removed.
+ mstore(0, d)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let c
+// let d
+// c := calldataload(0)
+// d := 1
+// if c
+// {
+// d := 2
+// }
+// mstore(0, d)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul
new file mode 100644
index 00000000..e47c31d1
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul
@@ -0,0 +1,24 @@
+{
+ let c
+ let d
+ c := calldataload(0)
+ // This assignment will be overwritten in all branches and thus can be removed.
+ d := 1
+ if c {
+ d := 2
+ }
+ d := 3
+ mstore(0, d)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let c
+// let d
+// c := calldataload(0)
+// if c
+// {
+// }
+// d := 3
+// mstore(0, d)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul
new file mode 100644
index 00000000..00065ed2
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul
@@ -0,0 +1,25 @@
+{
+ let c
+ let d
+ c := calldataload(0)
+ d := 1
+ if c {
+ // Uses the assignment above
+ d := d
+ }
+ d := 3
+ mstore(0, d)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let c
+// let d
+// c := calldataload(0)
+// d := 1
+// if c
+// {
+// }
+// d := 3
+// mstore(0, d)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul
new file mode 100644
index 00000000..26bcfc72
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul
@@ -0,0 +1,19 @@
+{
+ function f() -> a, b {}
+ let x, y
+ x := 1
+ x := 2
+ // Will not be used, but is a multi-assign, so not removed.
+ x, y := f()
+ x := 3
+ y := 4
+}
+// ----
+// redundantAssignEliminator
+// {
+// function f() -> a, b
+// {
+// }
+// let x, y
+// x, y := f()
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul
new file mode 100644
index 00000000..cf646126
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul
@@ -0,0 +1,15 @@
+{
+ let a := 2
+ a := 7
+ let b := 8
+ b := a
+ a := b
+}
+// ----
+// redundantAssignEliminator
+// {
+// let a := 2
+// a := 7
+// let b := 8
+// b := a
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul
new file mode 100644
index 00000000..ae3e5226
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul
@@ -0,0 +1,11 @@
+{
+ let a
+ a := 0
+ a := mload(0)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let a
+// a := mload(0)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul
new file mode 100644
index 00000000..702f854d
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul
@@ -0,0 +1,16 @@
+{
+ let a
+ {
+ let b
+ b := 2
+ a := 2
+ }
+}
+// ----
+// redundantAssignEliminator
+// {
+// let a
+// {
+// let b
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul
new file mode 100644
index 00000000..913a7694
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul
@@ -0,0 +1,10 @@
+{
+ let a
+ a := 1
+ a := 2
+}
+// ----
+// redundantAssignEliminator
+// {
+// let a
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul
new file mode 100644
index 00000000..96265576
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul
@@ -0,0 +1,22 @@
+{
+ let x
+ // Will be overwritten in all branches
+ x := 1
+ switch calldataload(0)
+ case 0 { x := 2 }
+ default { x := 3 }
+ mstore(x, 0)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let x
+// switch calldataload(0)
+// case 0 {
+// x := 2
+// }
+// default {
+// x := 3
+// }
+// mstore(x, 0)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul
new file mode 100644
index 00000000..cbe859ed
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul
@@ -0,0 +1,19 @@
+{
+ let x
+ // Will NOT be overwritten in all branches
+ x := 1
+ switch calldataload(0)
+ case 0 { x := 2 }
+ mstore(x, 0)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let x
+// x := 1
+// switch calldataload(0)
+// case 0 {
+// x := 2
+// }
+// mstore(x, 0)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul
new file mode 100644
index 00000000..1a3b26eb
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul
@@ -0,0 +1,23 @@
+{
+ let x
+ // Will be used in some and overwritten in others
+ x := 1
+ switch calldataload(0)
+ case 0 { x := 2 }
+ default { mstore(x, 1) }
+ mstore(x, 0)
+}
+// ----
+// redundantAssignEliminator
+// {
+// let x
+// x := 1
+// switch calldataload(0)
+// case 0 {
+// x := 2
+// }
+// default {
+// mstore(x, 1)
+// }
+// mstore(x, 0)
+// }
diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul
new file mode 100644
index 00000000..cc78b74d
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul
@@ -0,0 +1,16 @@
+{
+ let x
+ // Not referenced anywhere.
+ x := 1
+ switch calldataload(0)
+ case 0 { mstore(0, 1) }
+}
+// ----
+// redundantAssignEliminator
+// {
+// let x
+// switch calldataload(0)
+// case 0 {
+// mstore(0, 1)
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul
new file mode 100644
index 00000000..51d1627f
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul
@@ -0,0 +1,35 @@
+{
+ function copy(from, to) -> length {
+ length := mload(from)
+ mstore(to, length)
+ from := add(from, 0x20)
+ to := add(to, 0x20)
+ for { let x := 1 } lt(x, length) { x := add(x, 0x20) } {
+ mstore(add(to, x), mload(add(from, x)))
+ }
+ }
+}
+// ----
+// ssaPlusCleanup
+// {
+// function copy(from, to) -> length
+// {
+// let length_1 := mload(from)
+// length := length_1
+// mstore(to, length_1)
+// let from_1 := add(from, 0x20)
+// let to_1 := add(to, 0x20)
+// for {
+// let x_1 := 1
+// let x := x_1
+// }
+// lt(x, length_1)
+// {
+// let x_2 := add(x, 0x20)
+// x := x_2
+// }
+// {
+// mstore(add(to_1, x), mload(add(from_1, x)))
+// }
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul
new file mode 100644
index 00000000..ddb33aa0
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul
@@ -0,0 +1,17 @@
+{
+ let a := 1
+ a := 2
+ a := 3
+ a := 4
+ mstore(0, a)
+}
+// ----
+// ssaPlusCleanup
+// {
+// let a_1 := 1
+// let a := a_1
+// let a_2 := 2
+// let a_3 := 3
+// let a_4 := 4
+// mstore(0, a_4)
+// }
diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul
new file mode 100644
index 00000000..67a6c5d3
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul
@@ -0,0 +1,17 @@
+{
+ let a := 1
+ a := add(a, 2)
+ a := add(a, 3)
+ a := mload(add(a, 4))
+ mstore(0, a)
+}
+// ----
+// ssaPlusCleanup
+// {
+// let a_1 := 1
+// let a := a_1
+// let a_2 := add(a_1, 2)
+// let a_3 := add(a_2, 3)
+// let a_4 := mload(add(a_3, 4))
+// mstore(0, a_4)
+// }