diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 89db52d8d..91f818100 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -82,7 +82,6 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, vector AsmAnalyzer::operator()(Literal const& _literal) { expectValidType(_literal.type, _literal.location); - if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32) typeError( _literal.location, @@ -93,6 +92,13 @@ vector AsmAnalyzer::operator()(Literal const& _literal) else if (_literal.kind == LiteralKind::Boolean) yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); + if (!m_dialect.validTypeForLiteral(_literal.kind, _literal.value, _literal.type)) + typeError( + _literal.location, + "Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"." + ); + + return {_literal.type}; } @@ -179,7 +185,8 @@ void AsmAnalyzer::operator()(Assignment const& _assignment) ); for (size_t i = 0; i < numVariables; ++i) - checkAssignment(_assignment.variableNames[i]); + if (i < types.size()) + checkAssignment(_assignment.variableNames[i], types[i]); } void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) @@ -193,6 +200,9 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) yul::IdentifierContext::VariableDeclaration, m_currentScope->insideFunction() ); + for (auto const& variable: _varDecl.variables) + expectValidType(variable.type, variable.location); + if (_varDecl.value) { vector types = std::visit(*this, *_varDecl.value); @@ -204,15 +214,25 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) to_string(types.size()) + " values." ); + + for (size_t i = 0; i < _varDecl.variables.size(); ++i) + { + YulString givenType = m_dialect.defaultType; + if (i < types.size()) + givenType = types[i]; + TypedName const& variable = _varDecl.variables[i]; + if (variable.type != givenType) + typeError( + variable.location, + "Assigning value of type \"" + givenType.str() + "\" to variable of type \"" + variable.type.str() + "." + ); + } } for (TypedName const& variable: _varDecl.variables) - { - expectValidType(variable.type, variable.location); m_activeVariables.insert(&std::get( m_currentScope->identifiers.at(variable.name)) ); - } } void AsmAnalyzer::operator()(FunctionDefinition const& _funDef) @@ -291,6 +311,11 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) ); } } + std::reverse(argTypes.begin(), argTypes.end()); + + if (parameterTypes && parameterTypes->size() == argTypes.size()) + for (size_t i = 0; i < parameterTypes->size(); ++i) + expectType((*parameterTypes)[i], argTypes[i], locationOf(_funCall.arguments[i])); if (m_success) { @@ -306,7 +331,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) void AsmAnalyzer::operator()(If const& _if) { - expectExpression(*_if.condition); + expectBoolExpression(*_if.condition); (*this)(_if.body); } @@ -315,32 +340,15 @@ void AsmAnalyzer::operator()(Switch const& _switch) { yulAssert(_switch.expression, ""); - expectExpression(*_switch.expression); - - YulString caseType; - bool mismatchingTypes = false; - for (auto const& _case: _switch.cases) - if (_case.value) - { - if (caseType.empty()) - caseType = _case.value->type; - else if (caseType != _case.value->type) - { - mismatchingTypes = true; - break; - } - } - if (mismatchingTypes) - m_errorReporter.typeError( - _switch.location, - "Switch cases have non-matching types." - ); + YulString valueType = expectExpression(*_switch.expression); set cases; for (auto const& _case: _switch.cases) { if (_case.value) { + expectType(valueType, _case.value->type, _case.value->location); + // We cannot use "expectExpression" here because *_case.value is not an // Expression and would be converted to an Expression otherwise. (*this)(*_case.value); @@ -366,8 +374,7 @@ void AsmAnalyzer::operator()(ForLoop const& _for) // condition, the body and the post part inside. m_currentScope = &scope(&_for.pre); - expectExpression(*_for.condition); - + expectBoolExpression(*_for.condition); // backup outer for-loop & create new state auto outerForLoop = m_currentForLoop; m_currentForLoop = &_for; @@ -403,10 +410,24 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr) return types.empty() ? m_dialect.defaultType : types.front(); } -void AsmAnalyzer::checkAssignment(Identifier const& _variable) +void AsmAnalyzer::expectBoolExpression(Expression const& _expr) +{ + YulString type = expectExpression(_expr); + if (type != m_dialect.boolType) + typeError(locationOf(_expr), + "Expected a value of boolean type \"" + + m_dialect.boolType.str() + + "\" but got \"" + + type.str() + + "\"" + ); +} + +void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueType) { yulAssert(!_variable.name.empty(), ""); size_t numErrorsBefore = m_errorReporter.errors().size(); + YulString const* variableType = nullptr; bool found = false; if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) { @@ -418,6 +439,8 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable) _variable.location, "Variable " + _variable.name.str() + " used before it was declared." ); + else + variableType = &std::get(*var).type; found = true; } else if (m_resolver) @@ -427,6 +450,7 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable) if (variableSize != size_t(-1)) { found = true; + variableType = &m_dialect.defaultType; yulAssert(variableSize == 1, "Invalid stack size of external reference."); } } @@ -438,6 +462,17 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable) if (numErrorsBefore == m_errorReporter.errors().size()) declarationError(_variable.location, "Variable not found or variable not lvalue."); } + if (variableType && *variableType != _valueType) + typeError(_variable.location, + "Assigning a value of type \"" + + _valueType.str() + + "\" to a variable of type \"" + + variableType->str() + + "\"." + ); + + if (m_success) + yulAssert(variableType, ""); } Scope& AsmAnalyzer::scope(Block const* _block) @@ -447,15 +482,28 @@ Scope& AsmAnalyzer::scope(Block const* _block) yulAssert(scopePtr, "Scope requested but not present."); return *scopePtr; } + void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location) { - if (!_type.empty() && !m_dialect.types.count(_type)) - m_errorReporter.typeError( + if (!m_dialect.types.count(_type)) + typeError( _location, "\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)." ); } +void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, SourceLocation const& _location) +{ + if (_expectedType != _givenType) + typeError(_location, + "Expected a value of type \"" + + _expectedType.str() + + "\" but got \"" + + _givenType.str() + + "\"" + ); +} + bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location) { auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier)); diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index dded6d5ab..853f9f654 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -96,13 +96,18 @@ private: /// Visits the expression, expects that it evaluates to exactly one value and /// returns the type. Reports errors on errors and returns the default type. YulString expectExpression(Expression const& _expr); + /// Vists the expression and expects it to return a single boolean value. + /// Reports an error otherwise. + void expectBoolExpression(Expression const& _expr); bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location); - /// Verifies that a variable to be assigned to exists and can be assigned to. - void checkAssignment(Identifier const& _variable); + /// Verifies that a variable to be assigned to exists, can be assigned to + /// and has the same type as the value. + void checkAssignment(Identifier const& _variable, YulString _valueType); Scope& scope(Block const* _block); void expectValidType(YulString _type, langutil::SourceLocation const& _location); + void expectType(YulString _expectedType, YulString _givenType, langutil::SourceLocation const& _location); bool warnOnInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location); bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location); diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 12d2866c2..65269e9e0 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -154,6 +154,8 @@ add_library(yul optimiser/Suite.h optimiser/SyntacticalEquality.cpp optimiser/SyntacticalEquality.h + optimiser/TypeInfo.cpp + optimiser/TypeInfo.h optimiser/UnusedPruner.cpp optimiser/UnusedPruner.h optimiser/VarDeclInitializer.cpp diff --git a/libyul/Dialect.cpp b/libyul/Dialect.cpp index 6bc056a75..407fdea2f 100644 --- a/libyul/Dialect.cpp +++ b/libyul/Dialect.cpp @@ -19,9 +19,26 @@ */ #include +#include using namespace solidity::yul; using namespace std; +using namespace solidity::langutil; + +Literal Dialect::zeroLiteralForType(solidity::yul::YulString _type) const +{ + if (_type == boolType && _type != defaultType) + return {SourceLocation{}, LiteralKind::Boolean, "false"_yulstring, _type}; + return {SourceLocation{}, LiteralKind::Number, "0"_yulstring, _type}; +} + +bool Dialect::validTypeForLiteral(LiteralKind _kind, YulString, YulString _type) const +{ + if (_kind == LiteralKind::Boolean) + return _type == boolType; + else + return true; +} Dialect const& Dialect::yulDeprecated() { diff --git a/libyul/Dialect.h b/libyul/Dialect.h index c25ba1ac3..c137791ee 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -33,6 +33,8 @@ namespace solidity::yul class YulString; using Type = YulString; +enum class LiteralKind; +struct Literal; struct BuiltinFunction { @@ -52,15 +54,21 @@ struct Dialect: boost::noncopyable YulString defaultType; /// Type used for the literals "true" and "false". YulString boolType; - std::set types; + std::set types = {{}}; /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; } - virtual BuiltinFunction const* discardFunction() const { return nullptr; } - virtual BuiltinFunction const* equalityFunction() const { return nullptr; } + virtual BuiltinFunction const* discardFunction(YulString /* _type */) const { return nullptr; } + virtual BuiltinFunction const* equalityFunction(YulString /* _type */) const { return nullptr; } virtual BuiltinFunction const* booleanNegationFunction() const { return nullptr; } + /// Check whether the given type is legal for the given literal value. + /// Should only be called if the type exists in the dialect at all. + virtual bool validTypeForLiteral(LiteralKind _kind, YulString _value, YulString _type) const; + + virtual Literal zeroLiteralForType(YulString _type) const; + virtual std::set fixedFunctionNames() const { return {}; } Dialect() = default; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 39fc65632..27a2b85e3 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -290,6 +290,28 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA m_functions["u256_to_bool"_yulstring].returns = {"bool"_yulstring}; } +BuiltinFunctionForEVM const* EVMDialectTyped::discardFunction(YulString _type) const +{ + if (_type == "bool"_yulstring) + return builtin("popbool"_yulstring); + else + { + yulAssert(_type == defaultType, ""); + return builtin("pop"_yulstring); + } +} + +BuiltinFunctionForEVM const* EVMDialectTyped::equalityFunction(YulString _type) const +{ + if (_type == "bool"_yulstring) + return nullptr; + else + { + yulAssert(_type == defaultType, ""); + return builtin("eq"_yulstring); + } +} + EVMDialectTyped const& EVMDialectTyped::instance(langutil::EVMVersion _version) { static map> dialects; diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index 40842fa57..2141ab98f 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -68,8 +68,8 @@ struct EVMDialect: public Dialect /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. BuiltinFunctionForEVM const* builtin(YulString _name) const override; - BuiltinFunctionForEVM const* discardFunction() const override { return builtin("pop"_yulstring); } - BuiltinFunctionForEVM const* equalityFunction() const override { return builtin("eq"_yulstring); } + BuiltinFunctionForEVM const* discardFunction(YulString /*_type*/) const override { return builtin("pop"_yulstring); } + BuiltinFunctionForEVM const* equalityFunction(YulString /*_type*/) const override { return builtin("eq"_yulstring); } BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); } static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version); @@ -102,6 +102,10 @@ struct EVMDialectTyped: public EVMDialect /// Constructor, should only be used internally. Use the factory function below. EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess); + BuiltinFunctionForEVM const* discardFunction(YulString _type) const override; + BuiltinFunctionForEVM const* equalityFunction(YulString _type) const override; + BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("not"_yulstring); } + static EVMDialectTyped const& instance(langutil::EVMVersion _version); }; diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index 19bd98da7..dc69d913a 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -1235,7 +1235,7 @@ Object EVMToEwasmTranslator::run(Object const& _object) MainFunction{}(ast); ForLoopConditionIntoBody::run(context, ast); ExpressionSplitter::run(context, ast); - WordSizeTransform::run(m_dialect, WasmDialect::instance().defaultType, ast, nameDispenser); + WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser); NameDisplacer{nameDispenser, m_polyfillFunctions}(ast); for (auto const& st: m_polyfill->statements) @@ -1251,8 +1251,12 @@ Object EVMToEwasmTranslator::run(Object const& _object) AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.dataNames()); if (!analyzer.analyze(*ret.code)) { - // TODO the errors here are "wrong" because they have invalid source references! - string message; + string message = "Invalid code generated after EVM to wasm translation.\n"; + message += "Note that the source locations in the errors below will reference the original, not the translated code.\n"; + message += "Translated code:\n"; + message += "----------------------------------\n"; + message += ret.toString(&WasmDialect::instance()); + message += "----------------------------------\n"; for (auto const& err: errors) message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); yulAssert(false, message); diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 947326306..7f2149adc 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -91,8 +91,9 @@ WasmDialect::WasmDialect() m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; // Drop is actually overloaded for all types, but Yul does not support that. - // We could introduce "i32.drop". + // Because of that, we introduce "i32.drop". addFunction("drop", {i64}, {}); + addFunction("i32.drop", {i32}, {}); addFunction("nop", {}, {}); addFunction("unreachable", {}, {}, false); @@ -114,6 +115,22 @@ BuiltinFunction const* WasmDialect::builtin(YulString _name) const return nullptr; } +BuiltinFunction const* WasmDialect::discardFunction(YulString _type) const +{ + if (_type == "i32"_yulstring) + return builtin("i32.drop"_yulstring); + yulAssert(_type == "i64"_yulstring, ""); + return builtin("drop"_yulstring); +} + +BuiltinFunction const* WasmDialect::equalityFunction(YulString _type) const +{ + if (_type == "i32"_yulstring) + return builtin("i32.eq"_yulstring); + yulAssert(_type == "i64"_yulstring, ""); + return builtin("i64.eq"_yulstring); +} + WasmDialect const& WasmDialect::instance() { static std::unique_ptr dialect; diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index cf1a76d9d..1de65dfdc 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -35,19 +35,19 @@ struct Object; /** * Yul dialect for Wasm as a backend. * - * Builtin functions are a subset of the wasm instructions, always implicitly assuming - * unsigned 64 bit types. + * Builtin functions are a subset of the wasm instructions. + * + * There is a builtin function `i32.drop` that takes an i32, while `drop` takes i64. * - * !This is subject to changes! */ struct WasmDialect: public Dialect { WasmDialect(); BuiltinFunction const* builtin(YulString _name) const override; - BuiltinFunction const* discardFunction() const override { return builtin("drop"_yulstring); } - BuiltinFunction const* equalityFunction() const override { return builtin("i64.eq"_yulstring); } - BuiltinFunction const* booleanNegationFunction() const override { return builtin("i64.eqz"_yulstring); } + BuiltinFunction const* discardFunction(YulString _type) const override; + BuiltinFunction const* equalityFunction(YulString _type) const override; + BuiltinFunction const* booleanNegationFunction() const override { return builtin("i32.eqz"_yulstring); } std::set fixedFunctionNames() const override { return {"main"_yulstring}; } diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 9ebf31738..eec04fae6 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -45,7 +45,7 @@ void WordSizeTransform::operator()(FunctionCall& _fc) if (fun->literalArguments) { for (Expression& arg: _fc.arguments) - get(arg).type = m_defaultType; + get(arg).type = m_targetDialect.defaultType; return; } @@ -106,12 +106,17 @@ void WordSizeTransform::operator()(Block& _block) for (int i = 0; i < 3; i++) ret.push_back(VariableDeclaration{ varDecl.location, - {TypedName{varDecl.location, newLhs[i], m_defaultType}}, - make_unique(Literal{locationOf(*varDecl.value), LiteralKind::Number, "0"_yulstring, m_defaultType}) + {TypedName{varDecl.location, newLhs[i], m_targetDialect.defaultType}}, + make_unique(Literal{ + locationOf(*varDecl.value), + LiteralKind::Number, + "0"_yulstring, + m_targetDialect.defaultType + }) }); ret.push_back(VariableDeclaration{ varDecl.location, - {TypedName{varDecl.location, newLhs[3], m_defaultType}}, + {TypedName{varDecl.location, newLhs[3], m_targetDialect.defaultType}}, std::move(varDecl.value) }); return {std::move(ret)}; @@ -133,7 +138,7 @@ void WordSizeTransform::operator()(Block& _block) ret.push_back( VariableDeclaration{ varDecl.location, - {TypedName{varDecl.location, newLhs[i], m_defaultType}}, + {TypedName{varDecl.location, newLhs[i], m_targetDialect.defaultType}}, std::move(newRhs[i]) } ); @@ -163,7 +168,12 @@ void WordSizeTransform::operator()(Block& _block) ret.push_back(Assignment{ assignment.location, {Identifier{assignment.location, newLhs[i]}}, - make_unique(Literal{locationOf(*assignment.value), LiteralKind::Number, "0"_yulstring, m_defaultType}) + make_unique(Literal{ + locationOf(*assignment.value), + LiteralKind::Number, + "0"_yulstring, + m_targetDialect.defaultType + }) }); ret.push_back(Assignment{ assignment.location, @@ -209,14 +219,25 @@ void WordSizeTransform::operator()(Block& _block) void WordSizeTransform::run( Dialect const& _inputDialect, - YulString _targetDefaultType, + Dialect const& _targetDialect, Block& _ast, NameDispenser& _nameDispenser ) { // Free the name `or_bool`. NameDisplacer{_nameDispenser, {"or_bool"_yulstring}}(_ast); - WordSizeTransform{_inputDialect, _nameDispenser, _targetDefaultType}(_ast); + WordSizeTransform{_inputDialect, _targetDialect, _nameDispenser}(_ast); +} + +WordSizeTransform::WordSizeTransform( + Dialect const& _inputDialect, + Dialect const& _targetDialect, + NameDispenser& _nameDispenser +): + m_inputDialect(_inputDialect), + m_targetDialect(_targetDialect), + m_nameDispenser(_nameDispenser) +{ } void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) @@ -227,7 +248,7 @@ void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) { TypedNameList ret; for (auto newName: generateU64IdentifierNames(_n.name)) - ret.emplace_back(TypedName{_n.location, newName, m_defaultType}); + ret.emplace_back(TypedName{_n.location, newName, m_targetDialect.defaultType}); return ret; } ); @@ -291,7 +312,7 @@ vector WordSizeTransform::handleSwitchInternal( for (auto& c: cases) { - Literal label{_location, LiteralKind::Number, YulString(c.first.str()), m_defaultType}; + Literal label{_location, LiteralKind::Number, YulString(c.first.str()), m_targetDialect.defaultType}; ret.cases.emplace_back(Case{ c.second.front().location, make_unique(std::move(label)), @@ -312,7 +333,7 @@ vector WordSizeTransform::handleSwitchInternal( Assignment{ _location, {{_location, _runDefaultFlag}}, - make_unique(Literal{_location, LiteralKind::Number, "1"_yulstring, m_defaultType}) + make_unique(Literal{_location, LiteralKind::Boolean, "true"_yulstring, m_targetDialect.boolType}) } )} }); @@ -337,7 +358,7 @@ std::vector WordSizeTransform::handleSwitch(Switch& _switch) _switch.cases.pop_back(); ret.emplace_back(VariableDeclaration{ _switch.location, - {TypedName{_switch.location, runDefaultFlag, m_defaultType}}, + {TypedName{_switch.location, runDefaultFlag, m_targetDialect.boolType}}, {} }); } @@ -392,7 +413,7 @@ array, 4> WordSizeTransform::expandValue(Expression const lit.location, LiteralKind::Number, YulString(currentVal.str()), - m_defaultType + m_targetDialect.defaultType } ); } diff --git a/libyul/backends/wasm/WordSizeTransform.h b/libyul/backends/wasm/WordSizeTransform.h index 71e526b7f..67dfe8864 100644 --- a/libyul/backends/wasm/WordSizeTransform.h +++ b/libyul/backends/wasm/WordSizeTransform.h @@ -53,7 +53,7 @@ namespace solidity::yul * take four times the parameters and each of type u64. * In addition, it uses a single other builtin function called `or_bool` that * takes four u64 parameters and is supposed to return the logical disjunction - * of them as a u64 value. If this name is already used somewhere, it is renamed. + * of them as a i32 value. If this name is already used somewhere, it is renamed. * * Prerequisite: Disambiguator, ForLoopConditionIntoBody, ExpressionSplitter */ @@ -69,7 +69,7 @@ public: static void run( Dialect const& _inputDialect, - YulString _targetDefaultType, + Dialect const& _targetDialect, Block& _ast, NameDispenser& _nameDispenser ); @@ -77,13 +77,9 @@ public: private: explicit WordSizeTransform( Dialect const& _inputDialect, - NameDispenser& _nameDispenser, - YulString _defaultType - ): - m_inputDialect(_inputDialect), - m_nameDispenser(_nameDispenser), - m_defaultType(_defaultType) - { } + Dialect const& _targetDialect, + NameDispenser& _nameDispenser + ); void rewriteVarDeclList(std::vector&); void rewriteIdentifierList(std::vector&); @@ -103,8 +99,8 @@ private: std::vector expandValueToVector(Expression const& _e); Dialect const& m_inputDialect; + Dialect const& m_targetDialect; NameDispenser& m_nameDispenser; - YulString m_defaultType; /// maps original u256 variable's name to corresponding u64 variables' names std::map> m_variableMapping; }; diff --git a/libyul/optimiser/ConditionalSimplifier.cpp b/libyul/optimiser/ConditionalSimplifier.cpp index 858ac8999..453d2d9ce 100644 --- a/libyul/optimiser/ConditionalSimplifier.cpp +++ b/libyul/optimiser/ConditionalSimplifier.cpp @@ -77,12 +77,7 @@ void ConditionalSimplifier::operator()(Block& _block) Assignment{ location, {Identifier{location, condition}}, - make_unique(Literal{ - location, - LiteralKind::Number, - "0"_yulstring, - {} - }) + make_unique(m_dialect.zeroLiteralForType(m_dialect.boolType)) } ); } diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 8af8e6bb0..1640fa1b0 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -38,14 +39,13 @@ namespace ExpressionStatement makeDiscardCall( langutil::SourceLocation const& _location, - Dialect const& _dialect, + BuiltinFunction const& _discardFunction, Expression&& _expression ) { - yulAssert(_dialect.discardFunction(), "No discard function available."); return {_location, FunctionCall{ _location, - Identifier{_location, _dialect.discardFunction()->name}, + Identifier{_location, _discardFunction.name}, {std::move(_expression)} }}; } @@ -74,62 +74,12 @@ void removeEmptyCasesFromSwitch(Switch& _switchStmt) ); } -OptionalStatements reduceNoCaseSwitch(Dialect const& _dialect, Switch& _switchStmt) -{ - yulAssert(_switchStmt.cases.empty(), "Expected no case!"); - if (!_dialect.discardFunction()) - return {}; - - auto loc = locationOf(*_switchStmt.expression); - - return make_vector(makeDiscardCall( - loc, - _dialect, - std::move(*_switchStmt.expression) - )); -} - -OptionalStatements reduceSingleCaseSwitch(Dialect const& _dialect, Switch& _switchStmt) -{ - yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); - - auto& switchCase = _switchStmt.cases.front(); - auto loc = locationOf(*_switchStmt.expression); - if (switchCase.value) - { - if (!_dialect.equalityFunction()) - return {}; - return make_vector(If{ - std::move(_switchStmt.location), - make_unique(FunctionCall{ - loc, - Identifier{loc, _dialect.equalityFunction()->name}, - {std::move(*switchCase.value), std::move(*_switchStmt.expression)} - }), - std::move(switchCase.body) - }); - } - else - { - if (!_dialect.discardFunction()) - return {}; - - return make_vector( - makeDiscardCall( - loc, - _dialect, - std::move(*_switchStmt.expression) - ), - std::move(switchCase.body) - ); - } -} - } void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast) { - ControlFlowSimplifier{_context.dialect}(_ast); + TypeInfo typeInfo(_context.dialect, _ast); + ControlFlowSimplifier{_context.dialect, typeInfo}(_ast); } void ControlFlowSimplifier::operator()(Block& _block) @@ -194,12 +144,12 @@ void ControlFlowSimplifier::simplify(std::vector& _statements) GenericVisitor visitor{ VisitorFallback{}, [&](If& _ifStmt) -> OptionalStatements { - if (_ifStmt.body.statements.empty() && m_dialect.discardFunction()) + if (_ifStmt.body.statements.empty() && m_dialect.discardFunction(m_dialect.boolType)) { OptionalStatements s = vector{}; s->emplace_back(makeDiscardCall( _ifStmt.location, - m_dialect, + *m_dialect.discardFunction(m_dialect.boolType), std::move(*_ifStmt.condition) )); return s; @@ -211,9 +161,9 @@ void ControlFlowSimplifier::simplify(std::vector& _statements) removeEmptyCasesFromSwitch(_switchStmt); if (_switchStmt.cases.empty()) - return reduceNoCaseSwitch(m_dialect, _switchStmt); + return reduceNoCaseSwitch(_switchStmt); else if (_switchStmt.cases.size() == 1) - return reduceSingleCaseSwitch(m_dialect, _switchStmt); + return reduceSingleCaseSwitch(_switchStmt); return {}; } @@ -231,3 +181,58 @@ void ControlFlowSimplifier::simplify(std::vector& _statements) } ); } + +OptionalStatements ControlFlowSimplifier::reduceNoCaseSwitch(Switch& _switchStmt) const +{ + yulAssert(_switchStmt.cases.empty(), "Expected no case!"); + BuiltinFunction const* discardFunction = + m_dialect.discardFunction(m_typeInfo.typeOf(*_switchStmt.expression)); + if (!discardFunction) + return {}; + + auto loc = locationOf(*_switchStmt.expression); + + return make_vector(makeDiscardCall( + loc, + *discardFunction, + std::move(*_switchStmt.expression) + )); +} + +OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switchStmt) const +{ + yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); + + auto& switchCase = _switchStmt.cases.front(); + auto loc = locationOf(*_switchStmt.expression); + YulString type = m_typeInfo.typeOf(*_switchStmt.expression); + if (switchCase.value) + { + if (!m_dialect.equalityFunction(type)) + return {}; + return make_vector(If{ + std::move(_switchStmt.location), + make_unique(FunctionCall{ + loc, + Identifier{loc, m_dialect.equalityFunction(type)->name}, + {std::move(*switchCase.value), std::move(*_switchStmt.expression)} + }), + std::move(switchCase.body) + }); + } + else + { + if (!m_dialect.discardFunction(type)) + return {}; + + return make_vector( + makeDiscardCall( + loc, + *m_dialect.discardFunction(type), + std::move(*_switchStmt.expression) + ), + std::move(switchCase.body) + ); + } +} + diff --git a/libyul/optimiser/ControlFlowSimplifier.h b/libyul/optimiser/ControlFlowSimplifier.h index 5713f12a1..f8ea1af1e 100644 --- a/libyul/optimiser/ControlFlowSimplifier.h +++ b/libyul/optimiser/ControlFlowSimplifier.h @@ -23,6 +23,7 @@ namespace solidity::yul { struct Dialect; struct OptimiserStepContext; +class TypeInfo; /** * Simplifies several control-flow structures: @@ -61,11 +62,18 @@ public: void visit(Statement& _st) override; private: - ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {} + ControlFlowSimplifier(Dialect const& _dialect, TypeInfo const& _typeInfo): + m_dialect(_dialect), + m_typeInfo(_typeInfo) + {} void simplify(std::vector& _statements); + std::optional> reduceNoCaseSwitch(Switch& _switchStmt) const; + std::optional> reduceSingleCaseSwitch(Switch& _switchStmt) const; + Dialect const& m_dialect; + TypeInfo const& m_typeInfo; size_t m_numBreakStatements = 0; size_t m_numContinueStatements = 0; }; diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index d95e11a81..7b2ac14f6 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -23,11 +23,13 @@ #include #include +#include #include #include #include +#include #include @@ -39,7 +41,8 @@ using namespace solidity::langutil; void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast) { - ExpressionSplitter{_context.dialect, _context.dispenser}(_ast); + TypeInfo typeInfo(_context.dialect, _ast); + ExpressionSplitter{_context.dialect, _context.dispenser, typeInfo}(_ast); } void ExpressionSplitter::operator()(FunctionCall& _funCall) @@ -103,10 +106,13 @@ void ExpressionSplitter::outlineExpression(Expression& _expr) SourceLocation location = locationOf(_expr); YulString var = m_nameDispenser.newName({}); + YulString type = m_typeInfo.typeOf(_expr); m_statementsToPrefix.emplace_back(VariableDeclaration{ location, - {{TypedName{location, var, {}}}}, + {{TypedName{location, var, type}}}, make_unique(std::move(_expr)) }); _expr = Identifier{location, var}; + m_typeInfo.setVariableType(var, type); } + diff --git a/libyul/optimiser/ExpressionSplitter.h b/libyul/optimiser/ExpressionSplitter.h index dfd640ac3..107be2590 100644 --- a/libyul/optimiser/ExpressionSplitter.h +++ b/libyul/optimiser/ExpressionSplitter.h @@ -30,9 +30,9 @@ namespace solidity::yul { -class NameCollector; struct Dialect; struct OptimiserStepContext; +class TypeInfo; /** * Optimiser component that modifies an AST in place, turning complex @@ -68,8 +68,14 @@ public: void operator()(Block& _block) override; private: - explicit ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser): - m_dialect(_dialect), m_nameDispenser(_nameDispenser) + explicit ExpressionSplitter( + Dialect const& _dialect, + NameDispenser& _nameDispenser, + TypeInfo& _typeInfo + ): + m_dialect(_dialect), + m_nameDispenser(_nameDispenser), + m_typeInfo(_typeInfo) { } /// Replaces the expression by a variable if it is a function call or functional @@ -82,6 +88,7 @@ private: std::vector m_statementsToPrefix; Dialect const& m_dialect; NameDispenser& m_nameDispenser; + TypeInfo& m_typeInfo; }; } diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp index a45b33635..22c3e76dc 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.cpp +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -56,9 +56,9 @@ void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) _forLoop.condition = make_unique( Literal { loc, - LiteralKind::Number, - "1"_yulstring, - {} + LiteralKind::Boolean, + "true"_yulstring, + m_dialect.boolType } ); } diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 4b72e4ad9..32036184a 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -41,11 +42,11 @@ using namespace solidity::yul; void FullInliner::run(OptimiserStepContext& _context, Block& _ast) { - FullInliner{_ast, _context.dispenser}.run(); + FullInliner{_ast, _context.dispenser, _context.dialect}.run(); } -FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): - m_ast(_ast), m_nameDispenser(_dispenser) +FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect): + m_ast(_ast), m_nameDispenser(_dispenser), m_dialect(_dialect) { // Determine constants SSAValueTracker tracker; @@ -139,7 +140,7 @@ void FullInliner::updateCodeSize(FunctionDefinition const& _fun) void FullInliner::handleBlock(YulString _currentFunctionName, Block& _block) { - InlineModifier{*this, m_nameDispenser, _currentFunctionName}(_block); + InlineModifier{*this, m_nameDispenser, _currentFunctionName, m_dialect}(_block); } bool FullInliner::recursive(FunctionDefinition const& _fun) const @@ -198,7 +199,7 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC if (_value) varDecl.value = make_unique(std::move(*_value)); else - varDecl.value = make_unique(Literal{{}, LiteralKind::Number, YulString{"0"}, {}}); + varDecl.value = make_unique(m_dialect.zeroLiteralForType(varDecl.variables.front().type)); newStatements.emplace_back(std::move(varDecl)); }; diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index 788eea99b..81c608de8 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -69,7 +69,7 @@ class FullInliner: public ASTModifier { public: static constexpr char const* name{"FullInliner"}; - static void run(OptimiserStepContext&, Block& _ast); + static void run(OptimiserStepContext& _context, Block& _ast); /// Inlining heuristic. /// @param _callSite the name of the function in which the function call is located. @@ -89,7 +89,7 @@ public: void tentativelyUpdateCodeSize(YulString _function, YulString _callSite); private: - FullInliner(Block& _ast, NameDispenser& _dispenser); + FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect); void run(); void updateCodeSize(FunctionDefinition const& _fun); @@ -108,6 +108,7 @@ private: std::set m_constants; std::map m_functionSizes; NameDispenser& m_nameDispenser; + Dialect const& m_dialect; }; /** @@ -117,10 +118,11 @@ private: class InlineModifier: public ASTModifier { public: - InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, YulString _functionName): + InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, YulString _functionName, Dialect const& _dialect): m_currentFunction(std::move(_functionName)), m_driver(_driver), - m_nameDispenser(_nameDispenser) + m_nameDispenser(_nameDispenser), + m_dialect(_dialect) { } void operator()(Block& _block) override; @@ -132,6 +134,7 @@ private: YulString m_currentFunction; FullInliner& m_driver; NameDispenser& m_nameDispenser; + Dialect const& m_dialect; }; /** diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index 91454b9ee..948a0e6a6 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -27,6 +27,8 @@ #include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; @@ -42,8 +44,14 @@ namespace class IntroduceSSA: public ASTModifier { public: - explicit IntroduceSSA(NameDispenser& _nameDispenser, set const& _variablesToReplace): - m_nameDispenser(_nameDispenser), m_variablesToReplace(_variablesToReplace) + explicit IntroduceSSA( + NameDispenser& _nameDispenser, + set const& _variablesToReplace, + TypeInfo& _typeInfo + ): + m_nameDispenser(_nameDispenser), + m_variablesToReplace(_variablesToReplace), + m_typeInfo(_typeInfo) { } void operator()(Block& _block) override; @@ -51,6 +59,7 @@ public: private: NameDispenser& m_nameDispenser; set const& m_variablesToReplace; + TypeInfo const& m_typeInfo; }; @@ -83,10 +92,10 @@ void IntroduceSSA::operator()(Block& _block) { YulString oldName = var.name; YulString newName = m_nameDispenser.newName(oldName); - newVariables.emplace_back(TypedName{loc, newName, {}}); + newVariables.emplace_back(TypedName{loc, newName, var.type}); statements.emplace_back(VariableDeclaration{ loc, - {TypedName{loc, oldName, {}}}, + {TypedName{loc, oldName, var.type}}, make_unique(Identifier{loc, newName}) }); } @@ -110,7 +119,11 @@ void IntroduceSSA::operator()(Block& _block) { YulString oldName = var.name; YulString newName = m_nameDispenser.newName(oldName); - newVariables.emplace_back(TypedName{loc, newName, {}}); + newVariables.emplace_back(TypedName{ + loc, + newName, + m_typeInfo.typeOfVariable(oldName) + }); statements.emplace_back(Assignment{ loc, {Identifier{loc, oldName}}, @@ -136,9 +149,12 @@ class IntroduceControlFlowSSA: public ASTModifier public: explicit IntroduceControlFlowSSA( NameDispenser& _nameDispenser, - set const& _variablesToReplace + set const& _variablesToReplace, + TypeInfo const& _typeInfo ): - m_nameDispenser(_nameDispenser), m_variablesToReplace(_variablesToReplace) + m_nameDispenser(_nameDispenser), + m_variablesToReplace(_variablesToReplace), + m_typeInfo(_typeInfo) { } void operator()(FunctionDefinition& _function) override; @@ -153,6 +169,7 @@ private: set m_variablesInScope; /// Set of variables that do not have a specific value. set m_variablesToReassign; + TypeInfo const& m_typeInfo; }; void IntroduceControlFlowSSA::operator()(FunctionDefinition& _function) @@ -221,7 +238,7 @@ void IntroduceControlFlowSSA::operator()(Block& _block) YulString newName = m_nameDispenser.newName(toReassign); toPrepend.emplace_back(VariableDeclaration{ locationOf(_s), - {TypedName{locationOf(_s), newName, {}}}, + {TypedName{locationOf(_s), newName, m_typeInfo.typeOfVariable(toReassign)}}, make_unique(Identifier{locationOf(_s), toReassign}) }); assignedVariables.insert(toReassign); @@ -375,10 +392,11 @@ void PropagateValues::operator()(Block& _block) void SSATransform::run(OptimiserStepContext& _context, Block& _ast) { + TypeInfo typeInfo(_context.dialect, _ast); Assignments assignments; assignments(_ast); - IntroduceSSA{_context.dispenser, assignments.names()}(_ast); - IntroduceControlFlowSSA{_context.dispenser, assignments.names()}(_ast); + IntroduceSSA{_context.dispenser, assignments.names(), typeInfo}(_ast); + IntroduceControlFlowSSA{_context.dispenser, assignments.names(), typeInfo}(_ast); PropagateValues{assignments.names()}(_ast); } diff --git a/libyul/optimiser/TypeInfo.cpp b/libyul/optimiser/TypeInfo.cpp new file mode 100644 index 000000000..ea2d81a83 --- /dev/null +++ b/libyul/optimiser/TypeInfo.cpp @@ -0,0 +1,103 @@ +/* + 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 . +*/ +/** + * Helper class that keeps track of the types while performing optimizations. + */ + +#include + +#include + +#include +#include + +#include + +using namespace std; +using namespace solidity::yul; +using namespace solidity::util; + +class TypeInfo::TypeCollector: public ASTWalker +{ +public: + explicit TypeCollector(Block const& _block) + { + (*this)(_block); + } + + using ASTWalker::operator(); + void operator()(VariableDeclaration const& _varDecl) override + { + for (auto const& var: _varDecl.variables) + variableTypes[var.name] = var.type; + } + void operator()(FunctionDefinition const& _funDef) override + { + ASTWalker::operator()(_funDef); + + auto& funType = functionTypes[_funDef.name]; + for (auto const arg: _funDef.parameters) + { + funType.parameters.emplace_back(arg.type); + variableTypes[arg.name] = arg.type; + } + for (auto const ret: _funDef.returnVariables) + { + funType.returns.emplace_back(ret.type); + variableTypes[ret.name] = ret.type; + } + } + + std::map variableTypes; + std::map functionTypes; +}; + + +TypeInfo::TypeInfo(Dialect const& _dialect, Block const& _ast): + m_dialect(_dialect) +{ + TypeCollector types(_ast); + m_functionTypes = std::move(types.functionTypes); + m_variableTypes = std::move(types.variableTypes); +} + +YulString TypeInfo::typeOf(Expression const& _expression) const +{ + return std::visit(GenericVisitor{ + [&](FunctionCall const& _funCall) { + YulString name = _funCall.functionName.name; + vector const* retTypes = nullptr; + if (BuiltinFunction const* fun = m_dialect.builtin(name)) + retTypes = &fun->returns; + else + retTypes = &m_functionTypes.at(name).returns; + yulAssert(retTypes && retTypes->size() == 1, "Call to typeOf for non-single-value expression."); + return retTypes->front(); + }, + [&](Identifier const& _identifier) { + return m_variableTypes.at(_identifier.name); + }, + [&](Literal const& _literal) { + return _literal.type; + } + }, _expression); +} + +YulString TypeInfo::typeOfVariable(YulString _name) const +{ + return m_variableTypes.at(_name); +} diff --git a/libyul/optimiser/TypeInfo.h b/libyul/optimiser/TypeInfo.h new file mode 100644 index 000000000..48c6c1fba --- /dev/null +++ b/libyul/optimiser/TypeInfo.h @@ -0,0 +1,64 @@ +/* + 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 . +*/ +/** + * Helper class that keeps track of the types while performing optimizations. + */ +#pragma once + +#include +#include + +#include +#include + +namespace solidity::yul +{ +struct Dialect; + +/** + * Helper class that keeps track of the types while performing optimizations. + * + * Only works on disambiguated sources! + */ +class TypeInfo +{ +public: + TypeInfo(Dialect const& _dialect, Block const& _ast); + + void setVariableType(YulString _name, YulString _type) { m_variableTypes[_name] = _type; } + + /// @returns the type of an expression that is assumed to return exactly one value. + YulString typeOf(Expression const& _expression) const; + + /// \returns the type of variable + YulString typeOfVariable(YulString _name) const; + +private: + class TypeCollector; + + struct FunctionType + { + std::vector parameters; + std::vector returns; + }; + + Dialect const& m_dialect; + std::map m_variableTypes; + std::map m_functionTypes; +}; + +} diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index b7925790a..9dc7cedc3 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -100,10 +100,10 @@ void UnusedPruner::operator()(Block& _block) subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); statement = Block{std::move(varDecl.location), {}}; } - else if (varDecl.variables.size() == 1 && m_dialect.discardFunction()) + else if (varDecl.variables.size() == 1 && m_dialect.discardFunction(varDecl.variables.front().type)) statement = ExpressionStatement{varDecl.location, FunctionCall{ varDecl.location, - {varDecl.location, m_dialect.discardFunction()->name}, + {varDecl.location, m_dialect.discardFunction(varDecl.variables.front().type)->name}, {*std::move(varDecl.value)} }}; } diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index bad601f0e..66c5b4505 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -20,6 +20,7 @@ #include #include +#include using namespace std; using namespace solidity; @@ -32,14 +33,14 @@ void VarDeclInitializer::operator()(Block& _block) using OptionalStatements = std::optional>; util::GenericVisitor visitor{ util::VisitorFallback{}, - [](VariableDeclaration& _varDecl) -> OptionalStatements + [this](VariableDeclaration& _varDecl) -> OptionalStatements { if (_varDecl.value) return {}; - Literal zero{{}, LiteralKind::Number, YulString{"0"}, {}}; + if (_varDecl.variables.size() == 1) { - _varDecl.value = make_unique(std::move(zero)); + _varDecl.value = make_unique(m_dialect.zeroLiteralForType(_varDecl.variables.front().type)); return {}; } else @@ -47,7 +48,10 @@ void VarDeclInitializer::operator()(Block& _block) OptionalStatements ret{vector{}}; langutil::SourceLocation loc{std::move(_varDecl.location)}; for (auto& var: _varDecl.variables) - ret->emplace_back(VariableDeclaration{loc, {std::move(var)}, make_unique(zero)}); + { + unique_ptr expr = make_unique(m_dialect.zeroLiteralForType(var.type)); + ret->emplace_back(VariableDeclaration{loc, {std::move(var)}, std::move(expr)}); + } return ret; } } diff --git a/libyul/optimiser/VarDeclInitializer.h b/libyul/optimiser/VarDeclInitializer.h index 556ce081b..75bb1493c 100644 --- a/libyul/optimiser/VarDeclInitializer.h +++ b/libyul/optimiser/VarDeclInitializer.h @@ -34,9 +34,14 @@ class VarDeclInitializer: public ASTModifier { public: static constexpr char const* name{"VarDeclInitializer"}; - static void run(OptimiserStepContext&, Block& _ast) { VarDeclInitializer{}(_ast); } + static void run(OptimiserStepContext& _ctx, Block& _ast) { VarDeclInitializer{_ctx.dialect}(_ast); } void operator()(Block& _block) override; + +private: + explicit VarDeclInitializer(Dialect const& _dialect): m_dialect(_dialect) {} + + Dialect const& m_dialect; }; } diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp index e564ac2b8..de47f4885 100644 --- a/test/libyul/Inliner.cpp +++ b/test/libyul/Inliner.cpp @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(simple_inside_structures) BOOST_CHECK_EQUAL(inlinableFunctions("{" "function g(a:u256) -> b:u256 { b := a }" "for {" - "} 1:u256 {" + "} true {" "function f() -> x:u256 { x := g(2:u256) }" "}" "{" diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 8ec551258..020f3d4c4 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -245,16 +245,6 @@ BOOST_AUTO_TEST_CASE(optional_types) BOOST_CHECK(successParse("{ function f(a:u256) -> b {} }")); } -BOOST_AUTO_TEST_CASE(invalid_types) -{ - /// testing invalid literal - /// NOTE: these will need to change when types are compared - CHECK_ERROR("{ let x:bool := 1:invalid }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); - /// testing invalid variable declaration - CHECK_ERROR("{ let x:invalid := 1:bool }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); - CHECK_ERROR("{ function f(a:invalid) {} }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); -} - BOOST_AUTO_TEST_CASE(number_literals) { BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); @@ -268,7 +258,7 @@ BOOST_AUTO_TEST_CASE(builtin_types) { BOOST_CHECK(successParse("{ let x:bool := true:bool }")); BOOST_CHECK(successParse("{ let x:u8 := 1:u8 }")); - BOOST_CHECK(successParse("{ let x:s8 := 1:u8 }")); + BOOST_CHECK(successParse("{ let x:s8 := 1:s8 }")); BOOST_CHECK(successParse("{ let x:u32 := 1:u32 }")); BOOST_CHECK(successParse("{ let x:s32 := 1:s32 }")); BOOST_CHECK(successParse("{ let x:u64 := 1:u64 }")); @@ -495,15 +485,7 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid) { CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected."); CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'"); - // TODO change this to an error once we check types. - BOOST_CHECK(successParse("{ if 42:u256 { } }")); -} - -BOOST_AUTO_TEST_CASE(switch_case_types) -{ - CHECK_ERROR("{ switch 0:u256 case 0:u256 {} case 1:u32 {} }", TypeError, "Switch cases have non-matching types."); - // The following should be an error in the future, but this is not yet detected. - BOOST_CHECK(successParse("{ switch 0:u256 case 0:u32 {} case 1:u32 {} }")); + CHECK_ERROR("{ if 42:u256 { } }", TypeError, "Expected a value of boolean type"); } BOOST_AUTO_TEST_CASE(switch_duplicate_case) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 3ac745d6f..e488460fc 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -111,6 +111,8 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename) m_dialect = &WasmDialect::instance(); else if (dialectName == "evm") m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()); + else if (dialectName == "evmTyped") + m_dialect = &EVMDialectTyped::instance(solidity::test::CommonOptions::get().evmVersion()); else BOOST_THROW_EXCEPTION(runtime_error("Invalid dialect " + dialectName)); @@ -357,7 +359,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line { disambiguate(); ExpressionSplitter::run(*m_context, *m_ast); - WordSizeTransform::run(*m_dialect, ""_yulstring, *m_ast, *m_nameDispenser); + WordSizeTransform::run(*m_dialect, *m_dialect, *m_ast, *m_nameDispenser); } else if (m_optimizerStep == "fullSuite") { diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul new file mode 100644 index 000000000..f0b46072f --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul @@ -0,0 +1,18 @@ +{ + let y:bool := false + for {} true { } { + if y { break } + } +} +// ==== +// dialect: yul +// step: conditionalSimplifier +// ---- +// { +// let y:bool := false +// for { } true { } +// { +// if y { break } +// y := false +// } +// } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul new file mode 100644 index 000000000..ab523246e --- /dev/null +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul @@ -0,0 +1,18 @@ +{ + let y:i32 := 0:i32 + for {} true { } { + if y { break } + } +} +// ==== +// dialect: ewasm +// step: conditionalSimplifier +// ---- +// { +// let y:i32 := 0:i32 +// for { } true { } +// { +// if y { break } +// y := false +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul index ce79ef5e6..c6b246ad0 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul @@ -1,7 +1,8 @@ { { let a:u256, b:u256 } { - for { let a:u256 } a { a := a } { + function eq(x: u256, y: u256) -> z: bool {} + for { let a:u256 } eq(a, a) { a := a } { let b:u256 := a } } @@ -13,7 +14,9 @@ // { // { let a, b } // { -// for { let a_1 } a_1 { a_1 := a_1 } +// function eq(x, y) -> z:bool +// { } +// for { let a_1 } eq(a_1, a_1) { a_1 := a_1 } // { let b_2 := a_1 } // } // } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul b/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul new file mode 100644 index 000000000..37f40f5e6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul @@ -0,0 +1,42 @@ +{ + function fun(x: i32, y) -> t: i32, z: i32 { + z := i32.add(x, i32.add(z, z)) + + } + i64.store(i32.load(5:i32), i64.load(8:i32)) + let i := 0 + for {} i32.eqz(i32.load(9:i32)) { i := i64.add(i, 1) } { + let f: i32, g: i32 := fun(i32.load(1:i32), i64.load(i32.load(0: i32))) + } +} +// ==== +// dialect: ewasm +// step: expressionSplitter +// ---- +// { +// function fun(x:i32, y) -> t:i32, z:i32 +// { +// let _1:i32 := i32.add(z, z) +// z := i32.add(x, _1) +// } +// let _2:i32 := 8:i32 +// let _3 := i64.load(_2) +// let _4:i32 := 5:i32 +// let _5:i32 := i32.load(_4) +// i64.store(_5, _3) +// let i := 0 +// for { } +// i32.eqz(i32.load(9:i32)) +// { +// let _6 := 1 +// i := i64.add(i, _6) +// } +// { +// let _7:i32 := 0:i32 +// let _8:i32 := i32.load(_7) +// let _9 := i64.load(_8) +// let _10:i32 := 1:i32 +// let _11:i32 := i32.load(_10) +// let f:i32, g:i32 := fun(_11, _9) +// } +// } diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul index 9b401e638..95e171475 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul @@ -19,7 +19,7 @@ // { } // for { } a { } // { } -// for { } 1 { } +// for { } true { } // { // if iszero(add(a, a)) { break } // } diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul index 6a570750f..6f1ad5458 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul @@ -5,7 +5,7 @@ // step: forLoopConditionIntoBody // ---- // { -// for { let a := 1 } 1 { a := add(a, 1) } +// for { let a := 1 } true { a := add(a, 1) } // { // if iszero(iszero(eq(a, 10))) { break } // } diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul index 257467d28..292284a65 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul @@ -19,16 +19,16 @@ // { // let random := 42 // for { -// for { let a := 1 } 1 { } +// for { let a := 1 } true { } // { // if iszero(iszero(eq(a, 10))) { break } // a := add(a, 1) // } // let b := 1 // } -// 1 +// true // { -// for { let c := 1 } 1 { c := add(c, 1) } +// for { let c := 1 } true { c := add(c, 1) } // { // if iszero(iszero(eq(c, 2))) { break } // b := add(b, 1) diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul index 0e3c45ffe..f47026462 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul @@ -9,7 +9,7 @@ // ---- // { // let random := 42 -// for { let a := 1 } 1 { a := add(a, 1) } +// for { let a := 1 } true { a := add(a, 1) } // { // if iszero(iszero(eq(a, 10))) { break } // a := add(a, 1) diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul new file mode 100644 index 000000000..f9f01a193 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul @@ -0,0 +1,22 @@ +{ + function f(a: u256) -> x: bool, y:u256 { + y := mul(a, a) + } + let r: bool, s: u256 := f(mload(3)) +} +// ==== +// dialect: evmTyped +// step: fullInliner +// ---- +// { +// { +// let a_3 := mload(3) +// let x_4:bool := false +// let y_5 := 0 +// y_5 := mul(a_3, a_3) +// let r:bool := x_4 +// let s := y_5 +// } +// function f(a) -> x:bool, y +// { y := mul(a, a) } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul index 77c384021..4efe81d89 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul @@ -14,7 +14,7 @@ // { // let _1 := iszero(caller()) // for { } -// 1 +// true // { // for { } iszero(_1) { } // { } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed.yul new file mode 100644 index 000000000..f9d48a227 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed.yul @@ -0,0 +1,41 @@ +{ + let b:bool := true + let c:bool := false + c := b + b := false + + let a:u256 := 1 + a := add(a, 1) + if c { + a := add(a, 1) + } + a := add(a, 1) + mstore(a, 1) +} +// ==== +// dialect: evmTyped +// step: ssaTransform +// ---- +// { +// let b_1:bool := true +// let b:bool := b_1 +// let c_2:bool := false +// let c:bool := c_2 +// let c_3:bool := b_1 +// c := c_3 +// let b_4:bool := false +// b := b_4 +// let a_5 := 1 +// let a := a_5 +// let a_6 := add(a_5, 1) +// a := a_6 +// if c_3 +// { +// let a_7 := add(a_6, 1) +// a := a_7 +// } +// let a_9 := a +// let a_8 := add(a_9, 1) +// a := a_8 +// mstore(a_8, 1) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul new file mode 100644 index 000000000..685c42a24 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul @@ -0,0 +1,25 @@ +{ + let b:bool := true + let c:bool := false + for {} b {} { + c := true + } + let d: bool := c +} +// ==== +// dialect: evmTyped +// step: ssaTransform +// ---- +// { +// let b:bool := true +// let c_1:bool := false +// let c:bool := c_1 +// for { } b { } +// { +// let c_3:bool := c +// let c_2:bool := true +// c := c_2 +// } +// let c_4:bool := c +// let d:bool := c_4 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul new file mode 100644 index 000000000..7d61b1031 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul @@ -0,0 +1,25 @@ +{ + let b:bool := true + let c:bool := false + switch b + case true { c := true} + case false { } + let d: bool := c +} +// ==== +// dialect: evmTyped +// step: ssaTransform +// ---- +// { +// let b:bool := true +// let c_1:bool := false +// let c:bool := c_1 +// switch b +// case true { +// let c_2:bool := true +// c := c_2 +// } +// case false { } +// let c_3:bool := c +// let d:bool := c_3 +// } diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul new file mode 100644 index 000000000..912246bea --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul @@ -0,0 +1,23 @@ +{ + let a1 + let a2: bool + let b1, b2: bool + function f(a:u256, b:u256, c:bool) -> r:bool, t { + let x1: bool, x2 + } +} +// ==== +// dialect: evmTyped +// step: varDeclInitializer +// ---- +// { +// let a1 := 0 +// let a2:bool := false +// let b1 := 0 +// let b2:bool := false +// function f(a, b, c:bool) -> r:bool, t +// { +// let x1:bool := false +// let x2 := 0 +// } +// } diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul index 50a36f61f..617eebb64 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul @@ -67,13 +67,13 @@ // let _10_3 := 3 // sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3) // } -// default { run_default := 1 } +// default { run_default := true } // } -// default { run_default := 1 } +// default { run_default := true } // } -// default { run_default := 1 } +// default { run_default := true } // } -// default { run_default := 1 } +// default { run_default := true } // if run_default // { // let _11_0 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul index 106941302..20f81d614 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul @@ -45,9 +45,9 @@ // let _8_3 := 2 // sstore(_8_0, _8_1, _8_2, _8_3, _7_0, _7_1, _7_2, _7_3) // } -// default { run_default := 1 } +// default { run_default := true } // } -// default { run_default := 1 } +// default { run_default := true } // } // case 536870912 { // switch _2_2 @@ -75,13 +75,13 @@ // let _10_3 := 3 // sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3) // } -// default { run_default := 1 } +// default { run_default := true } // } -// default { run_default := 1 } +// default { run_default := true } // } -// default { run_default := 1 } +// default { run_default := true } // } -// default { run_default := 1 } +// default { run_default := true } // if run_default // { // let _11_0 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul index eb05e0a6c..2a42adefe 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul @@ -13,7 +13,7 @@ // let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3) // let run_default // switch _2_0 -// default { run_default := 1 } +// default { run_default := true } // if run_default // { // let _3_0 := 0 diff --git a/test/libyul/yulSyntaxTests/assignment_fail.yul b/test/libyul/yulSyntaxTests/assignment_fail.yul new file mode 100644 index 000000000..59c18bfd3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/assignment_fail.yul @@ -0,0 +1,12 @@ +{ + let x:u256 + let y := x + let z:bool + z := y + y := z +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (51-52): Assigning a value of type "u256" to a variable of type "bool". +// TypeError: (62-63): Assigning a value of type "bool" to a variable of type "u256". diff --git a/test/libyul/yulSyntaxTests/for_loop_condition.yul b/test/libyul/yulSyntaxTests/for_loop_condition.yul new file mode 100644 index 000000000..72ed7b71f --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_loop_condition.yul @@ -0,0 +1,7 @@ +{ + let x:bool + for {} x {} {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_loop_condition_fail.yul b/test/libyul/yulSyntaxTests/for_loop_condition_fail.yul new file mode 100644 index 000000000..903f5471a --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_loop_condition_fail.yul @@ -0,0 +1,8 @@ +{ + let x + for {} x {} {} +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (23-24): Expected a value of boolean type "bool" but got "u256" diff --git a/test/libyul/yulSyntaxTests/for_loop_condition_fail_ewasm.yul b/test/libyul/yulSyntaxTests/for_loop_condition_fail_ewasm.yul new file mode 100644 index 000000000..cbe266f9f --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_loop_condition_fail_ewasm.yul @@ -0,0 +1,8 @@ +{ + let x + for {} x {} {} +} +// ==== +// dialect: ewasm +// ---- +// TypeError: (23-24): Expected a value of boolean type "i32" but got "i64" diff --git a/test/libyul/yulSyntaxTests/invalid_type.yul b/test/libyul/yulSyntaxTests/invalid_type.yul new file mode 100644 index 000000000..ec16ebf01 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid_type.yul @@ -0,0 +1,7 @@ +{ + let x: invalidType +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (10-24): "invalidType" is not a valid type (user defined types are not yet supported). diff --git a/test/libyul/yulSyntaxTests/invalid_type2.yul b/test/libyul/yulSyntaxTests/invalid_type2.yul new file mode 100644 index 000000000..04593e666 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid_type2.yul @@ -0,0 +1,8 @@ +{ + let x := 1:invalidType +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (15-28): "invalidType" is not a valid type (user defined types are not yet supported). +// TypeError: (10-11): Assigning value of type "invalidType" to variable of type "u256. diff --git a/test/libyul/yulSyntaxTests/invalid_type3.yul b/test/libyul/yulSyntaxTests/invalid_type3.yul new file mode 100644 index 000000000..cf92ce7b8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid_type3.yul @@ -0,0 +1,8 @@ +{ + function f(a: invalidType) -> b: invalidType {} +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (17-31): "invalidType" is not a valid type (user defined types are not yet supported). +// TypeError: (36-50): "invalidType" is not a valid type (user defined types are not yet supported). diff --git a/test/libyul/yulSyntaxTests/invalid_type4.yul b/test/libyul/yulSyntaxTests/invalid_type4.yul new file mode 100644 index 000000000..faf217a88 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid_type4.yul @@ -0,0 +1,9 @@ +{ + switch 1 + case 8: invalidType {} +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (24-38): Expected a value of type "u256" but got "invalidType" +// TypeError: (24-38): "invalidType" is not a valid type (user defined types are not yet supported). diff --git a/test/libyul/yulSyntaxTests/type_check_cases.yul b/test/libyul/yulSyntaxTests/type_check_cases.yul new file mode 100644 index 000000000..4e09457c8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/type_check_cases.yul @@ -0,0 +1,8 @@ +{ + switch 7:i64 + case 0:i64 {} + case 2:i64 {} +} +// ==== +// dialect: ewasm +// ---- diff --git a/test/libyul/yulSyntaxTests/type_check_cases_fail.yul b/test/libyul/yulSyntaxTests/type_check_cases_fail.yul new file mode 100644 index 000000000..c799bec4b --- /dev/null +++ b/test/libyul/yulSyntaxTests/type_check_cases_fail.yul @@ -0,0 +1,10 @@ +{ + switch 7:i32 + case 0:i64 {} + case 2:i64 {} +} +// ==== +// dialect: ewasm +// ---- +// TypeError: (28-33): Expected a value of type "i32" but got "i64" +// TypeError: (46-51): Expected a value of type "i32" but got "i64" diff --git a/test/libyul/yulSyntaxTests/type_check_cases_fail_evmtyped.yul b/test/libyul/yulSyntaxTests/type_check_cases_fail_evmtyped.yul new file mode 100644 index 000000000..c1db211f0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/type_check_cases_fail_evmtyped.yul @@ -0,0 +1,10 @@ +{ + switch 7 + case true:bool {} + case true:bool {} +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (24-33): Expected a value of type "u256" but got "bool" +// TypeError: (46-55): Expected a value of type "u256" but got "bool" diff --git a/test/libyul/yulSyntaxTests/type_check_if_condition.yul b/test/libyul/yulSyntaxTests/type_check_if_condition.yul new file mode 100644 index 000000000..eaccf63f6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/type_check_if_condition.yul @@ -0,0 +1,7 @@ +{ + let x:i32 + if x {} +} +// ==== +// dialect: ewasm +// ---- diff --git a/test/libyul/yulSyntaxTests/type_check_if_condition_fail.yul b/test/libyul/yulSyntaxTests/type_check_if_condition_fail.yul new file mode 100644 index 000000000..a79d50e34 --- /dev/null +++ b/test/libyul/yulSyntaxTests/type_check_if_condition_fail.yul @@ -0,0 +1,8 @@ +{ + let x:i64 + if x {} +} +// ==== +// dialect: ewasm +// ---- +// TypeError: (23-24): Expected a value of boolean type "i32" but got "i64" diff --git a/test/libyul/yulSyntaxTests/user_defined_functions_fail.yul b/test/libyul/yulSyntaxTests/user_defined_functions_fail.yul new file mode 100644 index 000000000..324f937d4 --- /dev/null +++ b/test/libyul/yulSyntaxTests/user_defined_functions_fail.yul @@ -0,0 +1,12 @@ +{ + function f(a:u256, b:u256, c:bool) -> r:bool, t { + r := lt(a, b) + t := bool_to_u256(not(c)) + } + let x, y: bool := f(1, 2: u256, true) +} +// ==== +// dialect: evmTyped +// ---- +// TypeError: (126-127): Assigning value of type "bool" to variable of type "u256. +// TypeError: (129-136): Assigning value of type "u256" to variable of type "bool. diff --git a/test/libyul/yulSyntaxTests/user_defined_functions_fine.yul b/test/libyul/yulSyntaxTests/user_defined_functions_fine.yul new file mode 100644 index 000000000..e24ea923e --- /dev/null +++ b/test/libyul/yulSyntaxTests/user_defined_functions_fine.yul @@ -0,0 +1,10 @@ +{ + function f(a:u256, b:u256, c:bool) -> r:bool, t { + r := lt(a, b) + t := bool_to_u256(not(c)) + } + let x: bool, y: u256 := f(1, 2: u256, true) +} +// ==== +// dialect: evmTyped +// ----