diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libsolidity/analysis/ConstantEvaluator.cpp | 2 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 9 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 2 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 103 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 109 | ||||
-rw-r--r-- | libsolidity/codegen/AsmCodeGen.cpp | 194 | ||||
-rw-r--r-- | libsolidity/codegen/AsmCodeGen.h | 93 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.cpp | 4 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 11 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 75 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.h | 12 | ||||
-rw-r--r-- | libsolidity/formal/SMTChecker.cpp | 58 | ||||
-rw-r--r-- | libsolidity/formal/SymbolicVariables.cpp | 26 | ||||
-rw-r--r-- | libsolidity/formal/SymbolicVariables.h | 22 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.cpp | 53 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.h | 3 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 2 | ||||
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 22 |
20 files changed, 608 insertions, 195 deletions
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index dc4c6d15..43d61c86 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -23,6 +23,7 @@ set(sources ast/Types.cpp codegen/ABIFunctions.cpp codegen/ArrayUtils.cpp + codegen/AsmCodeGen.cpp codegen/Compiler.cpp codegen/CompilerContext.cpp codegen/CompilerUtils.cpp diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 9d041ce5..26d9584b 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -41,7 +41,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) auto right = type(_operation.rightExpression()); if (left && right) { - auto commonType = left->binaryOperatorResult(_operation.getOperator(), right); + TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right); if (!commonType) m_errorReporter.fatalTypeError( _operation.location(), diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index c4931d98..ac88a052 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -316,7 +316,14 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // We use the latest EVM version because we will re-run it anyway. yul::AsmAnalysisInfo analysisInfo; boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError; - yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); + yul::AsmAnalyzer( + analysisInfo, + errorsIgnored, + EVMVersion(), + errorTypeForLoose, + yul::Dialect::looseAssemblyForEVM(), + resolver + ).analyze(_inlineAssembly.operations()); return false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 9350df05..5c8e0c6c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -658,7 +658,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) m_errorReporter, m_evmVersion, Error::Type::SyntaxError, - yul::AsmFlavour::Loose, + yul::Dialect::looseAssemblyForEVM(), identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index cfb13271..e92134a8 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -724,7 +724,7 @@ bool ASTJsonConverter::visit(Literal const& _node) std::vector<pair<string, Json::Value>> attributes = { make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())), make_pair("value", value), - make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(_node.value())), + make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(asBytes(_node.value()))), make_pair( "subdenomination", subdenomination == Token::Illegal ? diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 6cadb5f3..fd8839ca 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -466,7 +466,7 @@ string AddressType::richIdentifier() const return "t_address"; } -bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const +BoolResult AddressType::isImplicitlyConvertibleTo(Type const& _other) const { if (_other.category() != category()) return false; @@ -475,7 +475,7 @@ bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const return other.m_stateMutability <= m_stateMutability; } -bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo)) return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable(); @@ -504,13 +504,13 @@ u256 AddressType::literalValue(Literal const* _literal) const return u256(_literal->valueWithoutUnderscores()); } -TypePointer AddressType::unaryOperatorResult(Token _operator) const +TypeResult AddressType::unaryOperatorResult(Token _operator) const { return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } -TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +TypeResult AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { // Addresses can only be compared. if (!TokenTraits::isCompareOp(_operator)) @@ -576,7 +576,7 @@ string IntegerType::richIdentifier() const return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits()); } -bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() == category()) { @@ -597,7 +597,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } -bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.category() == category() || _convertTo.category() == Category::Address || @@ -607,18 +607,17 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const _convertTo.category() == Category::FixedPoint; } -TypePointer IntegerType::unaryOperatorResult(Token _operator) const +TypeResult IntegerType::unaryOperatorResult(Token _operator) const { // "delete" is ok for all integer types if (_operator == Token::Delete) - return make_shared<TupleType>(); - // we allow +, -, ++ and -- - else if (_operator == Token::Add || _operator == Token::Sub || - _operator == Token::Inc || _operator == Token::Dec || - _operator == Token::BitNot) - return shared_from_this(); + return TypeResult{make_shared<TupleType>()}; + // we allow -, ++ and -- + else if (_operator == Token::Sub || _operator == Token::Inc || + _operator == Token::Dec || _operator == Token::BitNot) + return TypeResult{shared_from_this()}; else - return TypePointer(); + return TypeResult{""}; } bool IntegerType::operator==(Type const& _other) const @@ -651,7 +650,7 @@ bigint IntegerType::maxValue() const return (bigint(1) << m_bits) - 1; } -TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +TypeResult IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if ( _other->category() != Category::RationalNumber && @@ -704,7 +703,7 @@ string FixedPointType::richIdentifier() const return "t_" + string(isSigned() ? "" : "u") + "fixed" + to_string(m_totalBits) + "x" + to_string(m_fractionalDigits); } -bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() == category()) { @@ -717,18 +716,18 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } -bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.category() == category() || _convertTo.category() == Category::Integer; } -TypePointer FixedPointType::unaryOperatorResult(Token _operator) const +TypeResult FixedPointType::unaryOperatorResult(Token _operator) const { switch(_operator) { case Token::Delete: // "delete" is ok for all fixed types - return make_shared<TupleType>(); + return TypeResult(make_shared<TupleType>()); case Token::Add: case Token::Sub: case Token::Inc: @@ -771,7 +770,7 @@ bigint FixedPointType::minIntegerValue() const return bigint(0); } -TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +TypeResult FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { auto commonType = Type::commonType(shared_from_this(), _other); @@ -957,7 +956,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal return make_tuple(true, value); } -bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { switch (_convertTo.category()) { @@ -995,7 +994,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const } } -bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (isImplicitlyConvertibleTo(_convertTo)) return true; @@ -1008,7 +1007,7 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const return false; } -TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const +TypeResult RationalNumberType::unaryOperatorResult(Token _operator) const { rational value; switch (_operator) @@ -1029,10 +1028,10 @@ TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const default: return TypePointer(); } - return make_shared<RationalNumberType>(value); + return TypeResult(make_shared<RationalNumberType>(value)); } -TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +TypeResult RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) { @@ -1214,7 +1213,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096) return TypePointer(); - return make_shared<RationalNumberType>(value); + return TypeResult(make_shared<RationalNumberType>(value)); } } @@ -1354,7 +1353,7 @@ StringLiteralType::StringLiteralType(Literal const& _literal): { } -bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo)) return size_t(fixedBytes->numBytes()) >= m_value.size(); @@ -1409,7 +1408,7 @@ FixedBytesType::FixedBytesType(unsigned _bytes): m_bytes(_bytes) ); } -bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() != category()) return false; @@ -1417,7 +1416,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const return convertTo.m_bytes >= m_bytes; } -bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) || (_convertTo.category() == Category::Address && numBytes() == 20) || @@ -1425,18 +1424,18 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const _convertTo.category() == category(); } -TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const +TypeResult FixedBytesType::unaryOperatorResult(Token _operator) const { // "delete" and "~" is okay for FixedBytesType if (_operator == Token::Delete) - return make_shared<TupleType>(); + return TypeResult(make_shared<TupleType>()); else if (_operator == Token::BitNot) return shared_from_this(); return TypePointer(); } -TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +TypeResult FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (TokenTraits::isShiftOp(_operator)) { @@ -1452,7 +1451,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer co // FixedBytes can be compared and have bitwise operators applied to them if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator)) - return commonType; + return TypeResult(commonType); return TypePointer(); } @@ -1486,14 +1485,14 @@ u256 BoolType::literalValue(Literal const* _literal) const solAssert(false, "Bool type constructed from non-boolean literal."); } -TypePointer BoolType::unaryOperatorResult(Token _operator) const +TypeResult BoolType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) - return make_shared<TupleType>(); + return TypeResult(make_shared<TupleType>()); return (_operator == Token::Not) ? shared_from_this() : TypePointer(); } -TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +TypeResult BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (category() != _other->category()) return TypePointer(); @@ -1503,7 +1502,7 @@ TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _ return TypePointer(); } -bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (*this == _convertTo) return true; @@ -1520,7 +1519,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } -bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo)) return isPayable() || (addressType->stateMutability() < StateMutability::Payable); @@ -1533,14 +1532,14 @@ bool ContractType::isPayable() const return fallbackFunction && fallbackFunction->isPayable(); } -TypePointer ContractType::unaryOperatorResult(Token _operator) const +TypeResult ContractType::unaryOperatorResult(Token _operator) const { if (isSuper()) return TypePointer{}; return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } -TypePointer ReferenceType::unaryOperatorResult(Token _operator) const +TypeResult ReferenceType::unaryOperatorResult(Token _operator) const { if (_operator != Token::Delete) return TypePointer(); @@ -1551,7 +1550,7 @@ TypePointer ReferenceType::unaryOperatorResult(Token _operator) const case DataLocation::CallData: return TypePointer(); case DataLocation::Memory: - return make_shared<TupleType>(); + return TypeResult(make_shared<TupleType>()); case DataLocation::Storage: return m_isPointer ? TypePointer() : make_shared<TupleType>(); } @@ -1605,7 +1604,7 @@ string ReferenceType::identifierLocationSuffix() const return id; } -bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const +BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { if (_convertTo.category() != category()) return false; @@ -1645,7 +1644,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const } } -bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const +BoolResult ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const { if (isImplicitlyConvertibleTo(_convertTo)) return true; @@ -2006,7 +2005,7 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar return variablesAndOffsets; } -bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +BoolResult StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const { if (_convertTo.category() != category()) return false; @@ -2249,7 +2248,7 @@ bool StructType::recursive() const return *m_recursive; } -TypePointer EnumType::unaryOperatorResult(Token _operator) const +TypeResult EnumType::unaryOperatorResult(Token _operator) const { return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } @@ -2291,7 +2290,7 @@ size_t EnumType::numberOfMembers() const return m_enum.members().size(); }; -bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo == *this || _convertTo.category() == Category::Integer; } @@ -2308,7 +2307,7 @@ unsigned EnumType::memberValue(ASTString const& _member) const solAssert(false, "Requested unknown enum value " + _member); } -bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const +BoolResult TupleType::isImplicitlyConvertibleTo(Type const& _other) const { if (auto tupleType = dynamic_cast<TupleType const*>(&_other)) { @@ -2648,14 +2647,14 @@ bool FunctionType::operator==(Type const& _other) const return true; } -bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (m_kind == Kind::External && _convertTo == AddressType::address()) return true; return _convertTo.category() == category(); } -bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const +BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() != category()) return false; @@ -2680,14 +2679,14 @@ bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const return true; } -TypePointer FunctionType::unaryOperatorResult(Token _operator) const +TypeResult FunctionType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) - return make_shared<TupleType>(); + return TypeResult(make_shared<TupleType>()); return TypePointer(); } -TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +TypeResult 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 0f0548d3..39157abe 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -29,6 +29,7 @@ #include <libdevcore/Common.h> #include <libdevcore/CommonIO.h> +#include <libdevcore/Result.h> #include <boost/noncopyable.hpp> #include <boost/rational.hpp> @@ -50,6 +51,8 @@ using TypePointer = std::shared_ptr<Type const>; using FunctionTypePointer = std::shared_ptr<FunctionType const>; using TypePointers = std::vector<TypePointer>; using rational = boost::rational<dev::bigint>; +using TypeResult = Result<TypePointer>; +using BoolResult = Result<bool>; inline rational makeRational(bigint const& _numerator, bigint const& _denominator) { @@ -63,6 +66,7 @@ inline rational makeRational(bigint const& _numerator, bigint const& _denominato enum class DataLocation { Storage, CallData, Memory }; + /** * Helper class to compute storage offsets of members of structs and contracts. */ @@ -189,19 +193,19 @@ public: /// @returns an escaped identifier (will not contain any parenthesis or commas) static std::string escapeIdentifier(std::string const& _identifier); - virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } - virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const + virtual BoolResult isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } + virtual BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const { return isImplicitlyConvertibleTo(_convertTo); } /// @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) const { return TypePointer(); } + virtual TypeResult 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 _operator, TypePointer const& _other) const + virtual TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const { return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); } @@ -336,10 +340,10 @@ public: explicit AddressType(StateMutability _stateMutability); std::string richIdentifier() const override; - bool isImplicitlyConvertibleTo(Type const& _other) const override; - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _other) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override; bool operator==(Type const& _other) const override; @@ -381,10 +385,10 @@ public: explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned); std::string richIdentifier() const override; - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override; bool operator==(Type const& _other) const override; @@ -423,10 +427,10 @@ public: explicit FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, Modifier _modifier = Modifier::Unsigned); std::string richIdentifier() const override; - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override; bool operator==(Type const& _other) const override; @@ -476,11 +480,10 @@ public: explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()): m_value(_value), m_compatibleBytesType(_compatibleBytesType) {} - - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; @@ -536,8 +539,8 @@ public: explicit StringLiteralType(Literal const& _literal); - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - TypePointer binaryOperatorResult(Token, TypePointer const&) const override + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -570,12 +573,12 @@ public: explicit FixedBytesType(unsigned _bytes); - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override; unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } unsigned storageBytes() const override { return m_bytes; } @@ -601,8 +604,8 @@ public: BoolType() {} Category category() const override { return Category::Bool; } std::string richIdentifier() const override { return "t_bool"; } - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override; unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } unsigned storageBytes() const override { return 1; } @@ -624,8 +627,8 @@ public: explicit ReferenceType(DataLocation _location): m_location(_location) {} DataLocation location() const { return m_location; } - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token, TypePointer const&) const override + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -702,8 +705,8 @@ public: m_length(_length) {} - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; std::string richIdentifier() const override; bool operator==(const Type& _other) const override; unsigned calldataEncodedSize(bool _padded) const override; @@ -757,10 +760,10 @@ public: explicit ContractType(ContractDefinition const& _contract, bool _super = false): m_contract(_contract), m_super(_super) {} /// Contracts can be implicitly converted only to base contracts. - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; /// Contracts can only be explicitly converted to address types and base contracts. - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - TypePointer unaryOperatorResult(Token _operator) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypeResult unaryOperatorResult(Token _operator) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; unsigned calldataEncodedSize(bool _padded ) const override @@ -821,7 +824,7 @@ public: Category category() const override { return Category::Struct; } explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} - bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; + BoolResult isImplicitlyConvertibleTo(const Type& _convertTo) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; unsigned calldataEncodedSize(bool _padded) const override; @@ -876,7 +879,7 @@ class EnumType: public Type public: Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} - TypePointer unaryOperatorResult(Token _operator) const override; + TypeResult unaryOperatorResult(Token _operator) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; unsigned calldataEncodedSize(bool _padded) const override @@ -889,7 +892,7 @@ public: std::string canonicalName() const override; bool isValueType() const override { return true; } - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; TypePointer encodingType() const override { return std::make_shared<IntegerType>(8 * int(storageBytes())); @@ -917,10 +920,10 @@ class TupleType: public Type public: Category category() const override { return Category::Tuple; } explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {} - bool isImplicitlyConvertibleTo(Type const& _other) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _other) const override; std::string richIdentifier() const override; bool operator==(Type const& _other) const override; - TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } std::string toString(bool) const override; bool canBeStored() const override { return false; } u256 storageSize() const override; @@ -1065,10 +1068,10 @@ public: std::string richIdentifier() const override; bool operator==(Type const& _other) const override; - bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - TypePointer unaryOperatorResult(Token _operator) const override; - TypePointer binaryOperatorResult(Token, TypePointer const&) const override; + BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; + TypeResult unaryOperatorResult(Token _operator) const override; + TypeResult binaryOperatorResult(Token, TypePointer const&) const override; std::string canonicalName() const override; std::string toString(bool _short) const override; unsigned calldataEncodedSize(bool _padded) const override; @@ -1197,7 +1200,7 @@ public: std::string toString(bool _short) const override; std::string canonicalName() const override; bool canLiveOutsideStorage() const override { return false; } - TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } TypePointer encodingType() const override { return std::make_shared<IntegerType>(256); @@ -1230,7 +1233,7 @@ public: explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} TypePointer const& actualType() const { return m_actualType; } - TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } std::string richIdentifier() const override; bool operator==(Type const& _other) const override; bool canBeStored() const override { return false; } @@ -1255,7 +1258,7 @@ public: Category category() const override { return Category::Modifier; } explicit ModifierType(ModifierDefinition const& _modifier); - TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } bool canBeStored() const override { return false; } u256 storageSize() const override; bool canLiveOutsideStorage() const override { return false; } @@ -1281,7 +1284,7 @@ public: explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {} - TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } std::string richIdentifier() const override; bool operator==(Type const& _other) const override; bool canBeStored() const override { return false; } @@ -1308,7 +1311,7 @@ public: explicit MagicType(Kind _kind): m_kind(_kind) {} - TypePointer binaryOperatorResult(Token, TypePointer const&) const override + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -1339,9 +1342,9 @@ public: Category category() const override { return Category::InaccessibleDynamic; } std::string richIdentifier() const override { return "t_inaccessible"; } - bool isImplicitlyConvertibleTo(Type const&) const override { return false; } - bool isExplicitlyConvertibleTo(Type const&) const override { return false; } - TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } + BoolResult isImplicitlyConvertibleTo(Type const&) const override { return false; } + BoolResult isExplicitlyConvertibleTo(Type const&) const override { return false; } + TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } bool canBeStored() const override { return false; } bool canLiveOutsideStorage() const override { return false; } diff --git a/libsolidity/codegen/AsmCodeGen.cpp b/libsolidity/codegen/AsmCodeGen.cpp new file mode 100644 index 00000000..dfcc900b --- /dev/null +++ b/libsolidity/codegen/AsmCodeGen.cpp @@ -0,0 +1,194 @@ +/* + 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/>. +*/ +/** + * Adaptor between the abstract assembly and eth assembly. + */ + +#include <libsolidity/codegen/AsmCodeGen.h> + +#include <libyul/AsmData.h> +#include <libyul/AsmAnalysisInfo.h> + +#include <libyul/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> + +#include <libevmasm/Assembly.h> +#include <libevmasm/AssemblyItem.h> +#include <libevmasm/Instruction.h> + +#include <liblangutil/SourceLocation.h> + +#include <libdevcore/FixedHash.h> + +#include <memory> +#include <functional> + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace dev::solidity; + +EthAssemblyAdapter::EthAssemblyAdapter(eth::Assembly& _assembly): + m_assembly(_assembly) +{ +} + +void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location) +{ + m_assembly.setSourceLocation(_location); +} + +int EthAssemblyAdapter::stackHeight() const +{ + return m_assembly.deposit(); +} + +void EthAssemblyAdapter::appendInstruction(solidity::Instruction _instruction) +{ + m_assembly.append(_instruction); +} + +void EthAssemblyAdapter::appendConstant(u256 const& _constant) +{ + m_assembly.append(_constant); +} + +void EthAssemblyAdapter::appendLabel(LabelID _labelId) +{ + m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId)); +} + +void EthAssemblyAdapter::appendLabelReference(LabelID _labelId) +{ + m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId)); +} + +size_t EthAssemblyAdapter::newLabelId() +{ + return assemblyTagToIdentifier(m_assembly.newTag()); +} + +size_t EthAssemblyAdapter::namedLabel(std::string const& _name) +{ + return assemblyTagToIdentifier(m_assembly.namedTag(_name)); +} + +void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol) +{ + m_assembly.appendLibraryAddress(_linkerSymbol); +} + +void EthAssemblyAdapter::appendJump(int _stackDiffAfter) +{ + appendInstruction(solidity::Instruction::JUMP); + m_assembly.adjustDeposit(_stackDiffAfter); +} + +void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter) +{ + appendLabelReference(_labelId); + appendJump(_stackDiffAfter); +} + +void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId) +{ + appendLabelReference(_labelId); + appendInstruction(solidity::Instruction::JUMPI); +} + +void EthAssemblyAdapter::appendBeginsub(LabelID, int) +{ + // TODO we could emulate that, though + solAssert(false, "BEGINSUB not implemented for EVM 1.0"); +} + +void EthAssemblyAdapter::appendJumpsub(LabelID, int, int) +{ + // TODO we could emulate that, though + solAssert(false, "JUMPSUB not implemented for EVM 1.0"); +} + +void EthAssemblyAdapter::appendReturnsub(int, int) +{ + // TODO we could emulate that, though + solAssert(false, "RETURNSUB not implemented for EVM 1.0"); +} + +void EthAssemblyAdapter::appendAssemblySize() +{ + m_assembly.appendProgramSize(); +} + +pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly() +{ + shared_ptr<eth::Assembly> assembly{make_shared<eth::Assembly>()}; + auto sub = m_assembly.newSub(assembly); + return {make_shared<EthAssemblyAdapter>(*assembly), size_t(sub.data())}; +} + +void EthAssemblyAdapter::appendDataOffset(AbstractAssembly::SubID _sub) +{ + auto it = m_dataHashBySubId.find(_sub); + if (it == m_dataHashBySubId.end()) + m_assembly.pushSubroutineOffset(size_t(_sub)); + else + m_assembly << eth::AssemblyItem(eth::PushData, it->second); +} + +void EthAssemblyAdapter::appendDataSize(AbstractAssembly::SubID _sub) +{ + auto it = m_dataHashBySubId.find(_sub); + if (it == m_dataHashBySubId.end()) + m_assembly.pushSubroutineSize(size_t(_sub)); + else + m_assembly << u256(m_assembly.data(h256(it->second)).size()); +} + +AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data) +{ + eth::AssemblyItem pushData = m_assembly.newData(_data); + SubID subID = m_nextDataCounter++; + m_dataHashBySubId[subID] = pushData.data(); + return subID; +} + +EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(eth::AssemblyItem const& _tag) +{ + u256 id = _tag.data(); + solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large."); + return LabelID(id); +} + +void CodeGenerator::assemble( + Block const& _parsedData, + AsmAnalysisInfo& _analysisInfo, + eth::Assembly& _assembly, + ExternalIdentifierAccess const& _identifierAccess, + bool _useNamedLabelsForFunctions +) +{ + EthAssemblyAdapter assemblyAdapter(_assembly); + CodeTransform( + assemblyAdapter, + _analysisInfo, + false, + false, + _identifierAccess, + _useNamedLabelsForFunctions + )(_parsedData); +} diff --git a/libsolidity/codegen/AsmCodeGen.h b/libsolidity/codegen/AsmCodeGen.h new file mode 100644 index 00000000..4c6d97f4 --- /dev/null +++ b/libsolidity/codegen/AsmCodeGen.h @@ -0,0 +1,93 @@ +/* + 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/>. +*/ +/** + * Adaptor between the abstract assembly and eth assembly. + */ + +#pragma once + +#include <libyul/AsmAnalysis.h> +#include <libyul/backends/evm/AbstractAssembly.h> + +#include <liblangutil/SourceLocation.h> + +#include <functional> + +namespace yul +{ +struct Block; +} + +namespace dev +{ +namespace eth +{ +class Assembly; +class AssemblyItem; +} + +namespace solidity +{ + +class EthAssemblyAdapter: public yul::AbstractAssembly +{ +public: + explicit EthAssemblyAdapter(eth::Assembly& _assembly); + void setSourceLocation(langutil::SourceLocation const& _location) override; + int stackHeight() const override; + void appendInstruction(solidity::Instruction _instruction) override; + void appendConstant(u256 const& _constant) override; + void appendLabel(LabelID _labelId) override; + void appendLabelReference(LabelID _labelId) override; + size_t newLabelId() override; + size_t namedLabel(std::string const& _name) override; + void appendLinkerSymbol(std::string const& _linkerSymbol) override; + void appendJump(int _stackDiffAfter) override; + void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; + void appendJumpToIf(LabelID _labelId) override; + void appendBeginsub(LabelID, int) override; + void appendJumpsub(LabelID, int, int) override; + void appendReturnsub(int, int) override; + void appendAssemblySize() override; + std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override; + void appendDataOffset(SubID _sub) override; + void appendDataSize(SubID _sub) override; + SubID appendData(dev::bytes const& _data) override; + +private: + static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag); + + eth::Assembly& m_assembly; + std::map<SubID, dev::u256> m_dataHashBySubId; + size_t m_nextDataCounter = std::numeric_limits<size_t>::max() / 2; +}; + +class CodeGenerator +{ +public: + /// Performs code generation and appends generated to _assembly. + static void assemble( + yul::Block const& _parsedData, + yul::AsmAnalysisInfo& _analysisInfo, + dev::eth::Assembly& _assembly, + yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(), + bool _useNamedLabelsForFunctions = false + ); +}; + +} +} diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 55f1d252..fe57cff6 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -34,13 +34,13 @@ void Compiler::compileContract( bytes const& _metadata ) { - ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize); + ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns); runtimeCompiler.compileContract(_contract, _contracts); m_runtimeContext.appendAuxiliaryData(_metadata); // This might modify m_runtimeContext because it can access runtime functions at // creation time. - ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize); + ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1); m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts); m_context.optimise(m_optimize, m_optimizeRuns); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 5a3a233c..dac09c2e 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -24,10 +24,10 @@ #include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/ast/AST.h> #include <libsolidity/codegen/Compiler.h> +#include <libsolidity/codegen/AsmCodeGen.h> #include <libsolidity/interface/Version.h> #include <liblangutil/SourceReferenceFormatter.h> #include <libyul/AsmParser.h> -#include <libyul/AsmCodeGen.h> #include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysisInfo.h> #include <libyul/YulString.h> @@ -361,7 +361,7 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared<langutil::Scanner>(langutil::CharStream(_assembly, "--CODEGEN--")); - auto parserResult = yul::Parser(errorReporter, yul::AsmFlavour::Strict).parse(scanner, false); + auto parserResult = yul::Parser(errorReporter, yul::Dialect::strictAssemblyForEVM()).parse(scanner, false); #ifdef SOL_OUTPUT_ASM cout << yul::AsmPrinter()(*parserResult) << endl; #endif @@ -373,7 +373,7 @@ void CompilerContext::appendInlineAssembly( errorReporter, m_evmVersion, boost::none, - yul::AsmFlavour::Strict, + yul::Dialect::strictAssemblyForEVM(), identifierAccess.resolve ).analyze(*parserResult); if (!parserResult || !errorReporter.errors().empty() || !analyzerResult) @@ -386,8 +386,7 @@ void CompilerContext::appendInlineAssembly( for (auto const& error: errorReporter.errors()) message += SourceReferenceFormatter::formatExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](string const&) -> Scanner const& { return *scanner; } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); message += "-------------------------------------------\n"; @@ -395,7 +394,7 @@ void CompilerContext::appendInlineAssembly( } solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); - yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); + CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); // Reset the source location to the one of the node (instead of the CODEGEN source location) updateSourceLocation(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index aabdbb79..79c53a1c 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -23,8 +23,8 @@ #include <libsolidity/codegen/ContractCompiler.h> #include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/codegen/CompilerUtils.h> +#include <libsolidity/codegen/AsmCodeGen.h> #include <libsolidity/ast/AST.h> -#include <libyul/AsmCodeGen.h> #include <liblangutil/ErrorReporter.h> #include <libevmasm/Instruction.h> @@ -268,6 +268,70 @@ void ContractCompiler::appendDelegatecallCheck() // "We have not been called via DELEGATECALL". } +void ContractCompiler::appendInternalSelector( + map<FixedHash<4>, eth::AssemblyItem const> const& _entryPoints, + vector<FixedHash<4>> const& _ids, + eth::AssemblyItem const& _notFoundTag, + size_t _runs +) +{ + // Code for selecting from n functions without split: + // n times: dup1, push4 <id_i>, eq, push2/3 <tag_i>, jumpi + // push2/3 <notfound> jump + // (called SELECT[n]) + // Code for selecting from n functions with split: + // dup1, push4 <pivot>, gt, push2/3<tag_less>, jumpi + // SELECT[n/2] + // tag_less: + // SELECT[n/2] + // + // This means each split adds 16-18 bytes of additional code (note the additional jump out!) + // The average execution cost if we do not split at all are: + // (3 + 3 + 3 + 3 + 10) * n/2 = 24 * n/2 = 12 * n + // If we split once: + // (3 + 3 + 3 + 3 + 10) + 24 * n/4 = 24 * (n/4 + 1) = 6 * n + 24; + // + // We should split if + // _runs * 12 * n > _runs * (6 * n + 24) + 17 * createDataGas + // <=> _runs * 6 * (n - 4) > 17 * createDataGas + // + // Which also means that the execution itself is not profitable + // unless we have at least 5 functions. + + // Start with some comparisons to avoid overflow, then do the actual comparison. + bool split = false; + if (_ids.size() <= 4) + split = false; + else if (_runs > (17 * eth::GasCosts::createDataGas) / 6) + split = true; + else + split = (_runs * 6 * (_ids.size() - 4) > 17 * eth::GasCosts::createDataGas); + + if (split) + { + size_t pivotIndex = _ids.size() / 2; + FixedHash<4> pivot{_ids.at(pivotIndex)}; + m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(pivot)) << Instruction::GT; + eth::AssemblyItem lessTag{m_context.appendConditionalJump()}; + // Here, we have funid >= pivot + vector<FixedHash<4>> larger{_ids.begin() + pivotIndex, _ids.end()}; + appendInternalSelector(_entryPoints, larger, _notFoundTag, _runs); + m_context << lessTag; + // Here, we have funid < pivot + vector<FixedHash<4>> smaller{_ids.begin(), _ids.begin() + pivotIndex}; + appendInternalSelector(_entryPoints, smaller, _notFoundTag, _runs); + } + else + { + for (auto const& id: _ids) + { + m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(id)) << Instruction::EQ; + m_context.appendConditionalJumpTo(_entryPoints.at(id)); + } + m_context.appendJumpTo(_notFoundTag); + } +} + void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract) { map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions(); @@ -290,13 +354,14 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); // stack now is: <can-call-non-view-functions>? <funhash> + vector<FixedHash<4>> sortedIDs; for (auto const& it: interfaceFunctions) { callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); - m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << Instruction::EQ; - m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first)); + sortedIDs.emplace_back(it.first); } - m_context.appendJumpTo(notFound); + std::sort(sortedIDs.begin(), sortedIDs.end()); + appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimise_runs); m_context << notFound; if (fallback) @@ -618,7 +683,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } }; solAssert(_inlineAssembly.annotation().analysisInfo, ""); - yul::CodeGenerator::assemble( + CodeGenerator::assemble( _inlineAssembly.operations(), *_inlineAssembly.annotation().analysisInfo, m_context.nonConstAssembly(), diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 001aec7c..266ace0b 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -38,8 +38,9 @@ namespace solidity { class ContractCompiler: private ASTConstVisitor { public: - explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise): + explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise, size_t _optimise_runs = 200): m_optimise(_optimise), + m_optimise_runs(_optimise_runs), m_runtimeCompiler(_runtimeCompiler), m_context(_context) { @@ -81,6 +82,14 @@ private: /// This is done by inserting a specific push constant as the first instruction /// whose data will be modified in memory at deploy time. void appendDelegatecallCheck(); + /// Appends the function selector. Is called recursively to create a binary search tree. + /// @a _runs the number of intended executions of the contract to tune the split point. + void appendInternalSelector( + std::map<FixedHash<4>, eth::AssemblyItem const> const& _entryPoints, + std::vector<FixedHash<4>> const& _ids, + eth::AssemblyItem const& _notFoundTag, + size_t _runs + ); void appendFunctionSelector(ContractDefinition const& _contract); void appendCallValueCheck(); void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); @@ -122,6 +131,7 @@ private: void storeStackHeight(ASTNode const* _node); bool const m_optimise; + size_t const m_optimise_runs = 200; /// Pointer to the runtime compiler in case this is a creation compiler. ContractCompiler* m_runtimeCompiler = nullptr; CompilerContext& m_context; diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index ebb09f0a..17dc11ac 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -24,6 +24,8 @@ #include <liblangutil/ErrorReporter.h> +#include <libdevcore/StringUtils.h> + #include <boost/range/adaptor/map.hpp> #include <boost/algorithm/string/replace.hpp> @@ -136,13 +138,24 @@ bool SMTChecker::visit(IfStatement const& _node) return false; } +// Here we consider the execution of two branches: +// Branch 1 assumes the loop condition to be true and executes the loop once, +// after resetting touched variables. +// Branch 2 assumes the loop condition to be false and skips the loop after +// visiting the condition (it might contain side-effects, they need to be considered) +// and does not erase knowledge. +// If the loop is a do-while, condition side-effects are lost since the body, +// executed once before the condition, might reassign variables. +// Variables touched by the loop are merged with Branch 2. bool SMTChecker::visit(WhileStatement const& _node) { + auto indicesBeforeLoop = copyVariableIndices(); auto touchedVariables = m_variableUsage->touchedVariables(_node); resetVariables(touchedVariables); + decltype(indicesBeforeLoop) indicesAfterLoop; if (_node.isDoWhile()) { - visitBranch(_node.body()); + indicesAfterLoop = visitBranch(_node.body()); // TODO the assertions generated in the body should still be active in the condition _node.condition().accept(*this); if (isRootFunction()) @@ -154,19 +167,31 @@ bool SMTChecker::visit(WhileStatement const& _node) if (isRootFunction()) checkBooleanNotConstant(_node.condition(), "While loop condition is always $VALUE."); - visitBranch(_node.body(), expr(_node.condition())); + indicesAfterLoop = visitBranch(_node.body(), expr(_node.condition())); } - m_loopExecutionHappened = true; - resetVariables(touchedVariables); + // We reset the execution to before the loop + // and visit the condition in case it's not a do-while. + // A do-while's body might have non-precise information + // in its first run about variables that are touched. + resetVariableIndices(indicesBeforeLoop); + if (!_node.isDoWhile()) + _node.condition().accept(*this); + + mergeVariables(touchedVariables, expr(_node.condition()), indicesAfterLoop, copyVariableIndices()); + + m_loopExecutionHappened = true; return false; } +// Here we consider the execution of two branches similar to WhileStatement. bool SMTChecker::visit(ForStatement const& _node) { if (_node.initializationExpression()) _node.initializationExpression()->accept(*this); + auto indicesBeforeLoop = copyVariableIndices(); + // Do not reset the init expression part. auto touchedVariables = m_variableUsage->touchedVariables(_node.body()); @@ -193,13 +218,19 @@ bool SMTChecker::visit(ForStatement const& _node) _node.body().accept(*this); if (_node.loopExpression()) _node.loopExpression()->accept(*this); - m_interface->pop(); - m_loopExecutionHappened = true; + auto indicesAfterLoop = copyVariableIndices(); + // We reset the execution to before the loop + // and visit the condition. + resetVariableIndices(indicesBeforeLoop); + if (_node.condition()) + _node.condition()->accept(*this); - resetVariables(touchedVariables); + auto forCondition = _node.condition() ? expr(*_node.condition()) : smt::Expression(true); + mergeVariables(touchedVariables, forCondition, indicesAfterLoop, copyVariableIndices()); + m_loopExecutionHappened = true; return false; } @@ -271,14 +302,14 @@ void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _ checkCondition( _value < minValue(_type), _location, - "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")", + "Underflow (resulting value less than " + formatNumberReadable(_type.minValue()) + ")", "<result>", &_value ); checkCondition( _value > maxValue(_type), _location, - "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")", + "Overflow (resulting value larger than " + formatNumberReadable(_type.maxValue()) + ")", "<result>", &_value ); @@ -820,6 +851,7 @@ void SMTChecker::checkCondition( loopComment = "\nNote that some information is erased after the execution of loops.\n" "You can re-introduce information using require()."; + switch (result) { case smt::CheckResult::SATISFIABLE: @@ -838,19 +870,19 @@ void SMTChecker::checkCondition( for (auto const& eval: sortedModel) modelMessage << " " << eval.first << " = " << eval.second << "\n"; - m_errorReporter.warning(_location, message.str() + loopComment, SecondarySourceLocation().append(modelMessage.str(), SourceLocation())); + m_errorReporter.warning(_location, message.str(), SecondarySourceLocation().append(modelMessage.str(), SourceLocation()).append(loopComment, SourceLocation())); } else { message << "."; - m_errorReporter.warning(_location, message.str() + loopComment); + m_errorReporter.warning(_location, message.str(), SecondarySourceLocation().append(loopComment, SourceLocation())); } break; } case smt::CheckResult::UNSATISFIABLE: break; case smt::CheckResult::UNKNOWN: - m_errorReporter.warning(_location, _description + " might happen here." + loopComment); + m_errorReporter.warning(_location, _description + " might happen here.", SecondarySourceLocation().append(loopComment, SourceLocation())); break; case smt::CheckResult::CONFLICTING: m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); @@ -933,7 +965,7 @@ SMTChecker::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _exp try { // Parse and re-format nicely - value = formatNumber(bigint(value)); + value = formatNumberReadable(bigint(value)); } catch (...) { } } diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index efaeb97a..f7d2a119 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -37,16 +37,32 @@ SymbolicVariable::SymbolicVariable( { } +smt::Expression SymbolicVariable::currentValue() const +{ + return valueAtIndex(m_ssa->index()); +} + string SymbolicVariable::currentName() const { return uniqueSymbol(m_ssa->index()); } +smt::Expression SymbolicVariable::valueAtIndex(int _index) const +{ + return m_interface.newVariable(uniqueSymbol(_index), smtSort(*m_type)); +} + string SymbolicVariable::uniqueSymbol(unsigned _index) const { return m_uniqueName + "_" + to_string(_index); } +smt::Expression SymbolicVariable::increaseIndex() +{ + ++(*m_ssa); + return currentValue(); +} + SymbolicBoolVariable::SymbolicBoolVariable( TypePointer _type, string const& _uniqueName, @@ -57,11 +73,6 @@ SymbolicBoolVariable::SymbolicBoolVariable( solAssert(m_type->category() == Type::Category::Bool, ""); } -smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const -{ - return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Bool)); -} - SymbolicIntVariable::SymbolicIntVariable( TypePointer _type, string const& _uniqueName, @@ -72,11 +83,6 @@ SymbolicIntVariable::SymbolicIntVariable( solAssert(isNumber(m_type->category()), ""); } -smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const -{ - return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Int)); -} - SymbolicAddressVariable::SymbolicAddressVariable( string const& _uniqueName, smt::SolverInterface& _interface diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index fcf32760..ef40944c 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -46,20 +46,10 @@ public: virtual ~SymbolicVariable() = default; - smt::Expression currentValue() const - { - return valueAtIndex(m_ssa->index()); - } - + smt::Expression currentValue() const; std::string currentName() const; - - virtual smt::Expression valueAtIndex(int _index) const = 0; - - smt::Expression increaseIndex() - { - ++(*m_ssa); - return currentValue(); - } + virtual smt::Expression valueAtIndex(int _index) const; + smt::Expression increaseIndex(); unsigned index() const { return m_ssa->index(); } unsigned& index() { return m_ssa->index(); } @@ -86,9 +76,6 @@ public: std::string const& _uniqueName, smt::SolverInterface& _interface ); - -protected: - smt::Expression valueAtIndex(int _index) const; }; /** @@ -102,9 +89,6 @@ public: std::string const& _uniqueName, smt::SolverInterface& _interface ); - -protected: - smt::Expression valueAtIndex(int _index) const; }; /** diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index f5eb7e41..fbfb3472 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -22,12 +22,14 @@ #include <libsolidity/interface/AssemblyStack.h> +#include <libsolidity/codegen/AsmCodeGen.h> + #include <liblangutil/Scanner.h> #include <libyul/AsmPrinter.h> #include <libyul/AsmParser.h> #include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysisInfo.h> -#include <libyul/AsmCodeGen.h> +#include <libyul/backends/evm/EVMObjectCompiler.h> #include <libyul/backends/evm/EVMCodeTransform.h> #include <libyul/backends/evm/EVMAssembly.h> #include <libyul/ObjectParser.h> @@ -43,19 +45,19 @@ using namespace dev::solidity; namespace { -yul::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language) +yul::Dialect languageToDialect(AssemblyStack::Language _language) { switch (_language) { case AssemblyStack::Language::Assembly: - return yul::AsmFlavour::Loose; + return yul::Dialect::looseAssemblyForEVM(); case AssemblyStack::Language::StrictAssembly: - return yul::AsmFlavour::Strict; + return yul::Dialect::strictAssemblyForEVMObjects(); case AssemblyStack::Language::Yul: - return yul::AsmFlavour::Yul; + return yul::Dialect::yul(); } solAssert(false, ""); - return yul::AsmFlavour::Yul; + return yul::Dialect::yul(); } } @@ -72,7 +74,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string m_errors.clear(); m_analysisSuccessful = false; m_scanner = make_shared<Scanner>(CharStream(_source, _sourceName)); - m_parserResult = yul::ObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); + m_parserResult = yul::ObjectParser(m_errorReporter, languageToDialect(m_language)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); @@ -84,20 +86,42 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string void AssemblyStack::optimize() { solAssert(m_language != Language::Assembly, "Optimization requested for loose assembly."); - yul::OptimiserSuite::run(*m_parserResult->code, *m_parserResult->analysisInfo); + solAssert(m_analysisSuccessful, "Analysis was not successful."); + m_analysisSuccessful = false; + optimize(*m_parserResult); solAssert(analyzeParsed(), "Invalid source code after optimization."); } bool AssemblyStack::analyzeParsed() { solAssert(m_parserResult, ""); - solAssert(m_parserResult->code, ""); - m_parserResult->analysisInfo = make_shared<yul::AsmAnalysisInfo>(); - yul::AsmAnalyzer analyzer(*m_parserResult->analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); - m_analysisSuccessful = analyzer.analyze(*m_parserResult->code); + m_analysisSuccessful = analyzeParsed(*m_parserResult); return m_analysisSuccessful; } +bool AssemblyStack::analyzeParsed(yul::Object& _object) +{ + solAssert(_object.code, ""); + _object.analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer(*_object.analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToDialect(m_language)); + bool success = analyzer.analyze(*_object.code); + for (auto& subNode: _object.subObjects) + if (auto subObject = dynamic_cast<yul::Object*>(subNode.get())) + if (!analyzeParsed(*subObject)) + success = false; + return success; +} + +void AssemblyStack::optimize(yul::Object& _object) +{ + solAssert(_object.code, ""); + solAssert(_object.analysisInfo, ""); + for (auto& subNode: _object.subObjects) + if (auto subObject = dynamic_cast<yul::Object*>(subNode.get())) + optimize(*subObject); + yul::OptimiserSuite::run(*_object.code, *_object.analysisInfo); +} + MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { solAssert(m_analysisSuccessful, ""); @@ -111,7 +135,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; eth::Assembly assembly; - yul::CodeGenerator::assemble(*m_parserResult->code, *m_parserResult->analysisInfo, assembly); + EthAssemblyAdapter adapter(assembly); + yul::EVMObjectCompiler::compile(*m_parserResult, adapter, m_language == Language::Yul, false); object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble()); object.assembly = assembly.assemblyString(); return object; @@ -120,7 +145,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; yul::EVMAssembly assembly(true); - yul::CodeTransform(assembly, *m_parserResult->analysisInfo, m_language == Language::Yul, true)(*m_parserResult->code); + yul::EVMObjectCompiler::compile(*m_parserResult, assembly, m_language == Language::Yul, true); object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize()); /// TODO: fill out text representation return object; diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 0d04ffec..485ec1e7 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -83,6 +83,9 @@ public: private: bool analyzeParsed(); + bool analyzeParsed(yul::Object& _object); + + void optimize(yul::Object& _object); Language m_language = Language::Assembly; EVMVersion m_evmVersion; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 610caea1..1e6e2330 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -552,7 +552,7 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const { Json::Value methodIdentifiers(Json::objectValue); for (auto const& it: contractDefinition(_contractName).interfaceFunctions()) - methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref()); + methodIdentifiers[it.second->externalSignature()] = it.first.hex(); return methodIdentifiers; } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 0eef50d2..862b6633 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -69,12 +69,11 @@ Json::Value formatErrorWithException( bool const& _warning, string const& _type, string const& _component, - string const& _message, - function<Scanner const&(string const&)> const& _scannerFromSourceName + string const& _message ) { string message; - string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type, _scannerFromSourceName); + string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type); // NOTE: the below is partially a copy from SourceReferenceFormatter SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); @@ -433,8 +432,6 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value outputSelection = settings.get("outputSelection", Json::Value()); m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection)); - auto scannerFromSourceName = [&](string const& _sourceName) -> Scanner const& { return m_compilerStack.scanner(_sourceName); }; - try { m_compilerStack.compile(); @@ -448,8 +445,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) err.type() == Error::Type::Warning, err.typeName(), "general", - "", - scannerFromSourceName + "" )); } } @@ -461,8 +457,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, _error.typeName(), "general", - "Uncaught error: ", - scannerFromSourceName + "Uncaught error: " )); } /// This should not be leaked from compile(). @@ -482,8 +477,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, "CompilerError", "general", - "Compiler error (" + _exception.lineInfo() + ")", - scannerFromSourceName + "Compiler error (" + _exception.lineInfo() + ")" )); } catch (InternalCompilerError const& _exception) @@ -493,8 +487,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, "InternalCompilerError", "general", - "Internal compiler error (" + _exception.lineInfo() + ")", - scannerFromSourceName + "Internal compiler error (" + _exception.lineInfo() + ")" )); } catch (UnimplementedFeatureError const& _exception) @@ -504,8 +497,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, "UnimplementedFeatureError", "general", - "Unimplemented feature (" + _exception.lineInfo() + ")", - scannerFromSourceName + "Unimplemented feature (" + _exception.lineInfo() + ")" )); } catch (Exception const& _exception) |