diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/ConstantEvaluator.cpp | 6 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 5 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 22 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 472 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 69 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 38 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 8 | ||||
-rw-r--r-- | libsolidity/codegen/LValue.cpp | 4 | ||||
-rw-r--r-- | libsolidity/formal/Why3Translator.cpp | 12 | ||||
-rw-r--r-- | libsolidity/parsing/Token.cpp | 3 |
10 files changed, 535 insertions, 104 deletions
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 6beb655e..a86e3967 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -31,7 +31,7 @@ using namespace dev::solidity; void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { TypePointer const& subType = _operation.subExpression().annotation().type; - if (!dynamic_cast<IntegerConstantType const*>(subType.get())) + if (!dynamic_cast<ConstantNumberType const*>(subType.get())) BOOST_THROW_EXCEPTION(_operation.subExpression().createTypeError("Invalid constant expression.")); TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); _operation.annotation().type = t; @@ -41,9 +41,9 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { TypePointer const& leftType = _operation.leftExpression().annotation().type; TypePointer const& rightType = _operation.rightExpression().annotation().type; - if (!dynamic_cast<IntegerConstantType const*>(leftType.get())) + if (!dynamic_cast<ConstantNumberType const*>(leftType.get())) BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression.")); - if (!dynamic_cast<IntegerConstantType const*>(rightType.get())) + if (!dynamic_cast<ConstantNumberType const*>(rightType.get())) BOOST_THROW_EXCEPTION(_operation.rightExpression().createTypeError("Invalid constant expression.")); TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); if (Token::isCompareOp(_operation.getOperator())) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index d7542bf3..3351c716 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -103,10 +103,11 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) { if (!length->annotation().type) ConstantEvaluator e(*length); - - auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get()); + auto const* lengthType = dynamic_cast<ConstantNumberType const*>(length->annotation().type.get()); if (!lengthType) fatalTypeError(length->location(), "Invalid array length."); + else if (lengthType->denominator() != 1) + fatalTypeError(length->location(), "Invalid input for array length, expected integer."); else _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index bc342b58..ad93b38c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -773,8 +773,8 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) // Infer type from value. solAssert(!var.typeName(), ""); if ( - valueComponentType->category() == Type::Category::IntegerConstant && - !dynamic_pointer_cast<IntegerConstantType const>(valueComponentType)->integerType() + valueComponentType->category() == Type::Category::NumberConstant && + !dynamic_pointer_cast<ConstantNumberType const>(valueComponentType)->integerType() ) fatalTypeError(_statement.initialValue()->location(), "Invalid integer constant " + valueComponentType->toString() + "."); var.annotation().type = valueComponentType->mobileType(); @@ -799,8 +799,8 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) void TypeChecker::endVisit(ExpressionStatement const& _statement) { - if (type(_statement.expression())->category() == Type::Category::IntegerConstant) - if (!dynamic_pointer_cast<IntegerConstantType const>(type(_statement.expression()))->integerType()) + if (type(_statement.expression())->category() == Type::Category::NumberConstant) + if (!dynamic_pointer_cast<ConstantNumberType const>(type(_statement.expression()))->integerType()) typeError(_statement.expression().location(), "Invalid integer constant."); } @@ -1106,7 +1106,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto const& argType = type(*arguments[i]); if (functionType->takesArbitraryParameters()) { - if (auto t = dynamic_cast<IntegerConstantType const*>(argType.get())) + if (auto t = dynamic_cast<ConstantNumberType const*>(argType.get())) if (!t->integerType()) typeError(arguments[i]->location(), "Integer constant too large."); } @@ -1341,9 +1341,13 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get())) - if (!actualType.isDynamicallySized() && actualType.length() <= integerType->literalValue(nullptr)) + if (auto numberType = dynamic_cast<ConstantNumberType const*>(type(*index).get())) + { + if (numberType->denominator() != 1) + typeError(_access.location(), "Invalid type for array access."); + if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) typeError(_access.location(), "Out of bounds array access."); + } } resultType = actualType.baseType(); isLValue = actualType.location() != DataLocation::CallData; @@ -1368,7 +1372,7 @@ bool TypeChecker::visit(IndexAccess const& _access) else { index->accept(*this); - if (auto length = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + if (auto length = dynamic_cast<ConstantNumberType const*>(type(*index).get())) resultType = make_shared<TypeType>(make_shared<ArrayType>( DataLocation::Memory, typeType.actualType(), @@ -1387,7 +1391,7 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + if (auto integerType = dynamic_cast<ConstantNumberType const*>(type(*index).get())) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) typeError(_access.location(), "Out of bounds array access."); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index d541de23..b1a0f4e6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -32,6 +32,7 @@ using namespace std; using namespace dev; using namespace dev::solidity; +using rational = boost::rational<bigint>; void StorageOffsets::computeOffsets(TypePointers const& _types) { @@ -123,6 +124,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) Token::Value token = _type.token(); unsigned int m = _type.firstNumber(); + unsigned int n = _type.secondNumber(); switch (token) { @@ -132,10 +134,18 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) return make_shared<IntegerType>(m, IntegerType::Modifier::Unsigned); case Token::BytesM: return make_shared<FixedBytesType>(m); + case Token::FixedMxN: + return make_shared<FixedPointType>(m, n, FixedPointType::Modifier::Signed); + case Token::UFixedMxN: + return make_shared<FixedPointType>(m, n, FixedPointType::Modifier::Unsigned); case Token::Int: return make_shared<IntegerType>(256, IntegerType::Modifier::Signed); case Token::UInt: return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned); + case Token::Fixed: + return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Signed); + case Token::UFixed: + return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Unsigned); case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: @@ -171,9 +181,10 @@ TypePointer Type::forLiteral(Literal const& _literal) case Token::FalseLiteral: return make_shared<BoolType>(); case Token::Number: - if (!IntegerConstantType::isValidLiteral(_literal)) - return TypePointer(); - return make_shared<IntegerConstantType>(_literal); + if (ConstantNumberType::isValidLiteral(_literal)) + return make_shared<ConstantNumberType>(_literal); + else + return TypePointer(); case Token::StringLiteral: return make_shared<StringLiteralType>(_literal); default: @@ -246,17 +257,30 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.category() != category()) - return false; - IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); - if (convertTo.m_bits < m_bits) - return false; - if (isAddress()) - return convertTo.isAddress(); - else if (isSigned()) - return convertTo.isSigned(); + if (_convertTo.category() == category()) + { + IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); + if (convertTo.m_bits < m_bits) + return false; + if (isAddress()) + return convertTo.isAddress(); + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.m_bits > m_bits; + } + else if (_convertTo.category() == Category::FixedPoint) + { + FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); + if (convertTo.integerBits() < m_bits) + return false; + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.integerBits() > m_bits; + } else - return !convertTo.isSigned() || convertTo.m_bits > m_bits; + return false; } bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -302,7 +326,7 @@ string IntegerType::toString(bool) const TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->category() != Category::IntegerConstant && _other->category() != category()) + if (_other->category() != Category::NumberConstant && _other->category() != category()) return TypePointer(); auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other)); @@ -335,11 +359,134 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons return MemberList::MemberMap(); } -bool IntegerConstantType::isValidLiteral(const Literal& _literal) +FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPointType::Modifier _modifier): + m_integerBits(_integerBits), m_fractionalBits(_fractionalBits), m_modifier(_modifier) +{ + solAssert( + m_integerBits + m_fractionalBits > 0 && + m_integerBits + m_fractionalBits <= 256 && + m_integerBits % 8 == 0 && + m_fractionalBits % 8 == 0, + "Invalid bit number(s) for fixed type: " + + dev::toString(_integerBits) + "x" + dev::toString(_fractionalBits) + ); +} + +bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.category() == category()) + { + FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); + if (convertTo.m_integerBits < m_integerBits || convertTo.m_fractionalBits < m_fractionalBits) + return false; + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || (convertTo.m_integerBits > m_integerBits); + } + else if (_convertTo.category() == Category::Integer) + { + IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); + if (convertTo.numBits() < m_integerBits) + return false; + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.numBits() > m_integerBits; + } + else + return false; +} + +bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return _convertTo.category() == category() || + _convertTo.category() == Category::Integer || + _convertTo.category() == Category::Enum || + _convertTo.category() == Category::FixedBytes; +} + +TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const +{ + // "delete" is ok for all fixed types + if (_operator == Token::Delete) + return make_shared<TupleType>(); + // for fixed, we allow +, -, ++ and -- + else if ( + _operator == Token::Add || + _operator == Token::Sub || + _operator == Token::Inc || + _operator == Token::Dec || + _operator == Token::After + ) + return shared_from_this(); + else + return TypePointer(); +} + +bool FixedPointType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + FixedPointType const& other = dynamic_cast<FixedPointType const&>(_other); + return other.m_integerBits == m_integerBits && other.m_fractionalBits == m_fractionalBits && other.m_modifier == m_modifier; +} + +string FixedPointType::toString(bool) const +{ + string prefix = isSigned() ? "fixed" : "ufixed"; + return prefix + dev::toString(m_integerBits) + "x" + dev::toString(m_fractionalBits); +} + +TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + if (_other->category() != Category::NumberConstant + && _other->category() != category() + && _other->category() != Category::Integer + ) + return TypePointer(); + auto commonType = dynamic_pointer_cast<FixedPointType const>(Type::commonType(shared_from_this(), _other)); + + if (!commonType) + return TypePointer(); + + // All fixed types can be compared + if (Token::isCompareOp(_operator)) + return commonType; + if (Token::isBooleanOp(_operator)) + return TypePointer(); + return commonType; +} + +bool ConstantNumberType::isValidLiteral(Literal const& _literal) { try { - bigint x(_literal.value()); + rational numerator; + rational denominator(1); + auto radixPoint = find(_literal.value().begin(), _literal.value().end(), '.'); + if (radixPoint != _literal.value().end()) + { + //problem here. If the first digit is a 0 in the string, it won't + //turn it into a integer...Using find if not to count the leading 0s. + + auto leadingZeroes = find_if_not( + radixPoint + 1, + _literal.value().end(), + [](char const& a) { return a == '0'; } + ); + auto fractionalBegin = leadingZeroes != _literal.value().end() ? + leadingZeroes : radixPoint + 1; + denominator = bigint(string(fractionalBegin, _literal.value().end())); + denominator /= boost::multiprecision::pow( + bigint(10), + distance(radixPoint + 1, _literal.value().end()) + ); + numerator = bigint(string(_literal.value().begin(), radixPoint)); + rational x = numerator + denominator; + } + else + rational x = bigint(_literal.value()); } catch (...) { @@ -348,16 +495,40 @@ bool IntegerConstantType::isValidLiteral(const Literal& _literal) return true; } -IntegerConstantType::IntegerConstantType(Literal const& _literal) +ConstantNumberType::ConstantNumberType(Literal const& _literal) { - m_value = bigint(_literal.value()); + rational numerator; + rational denominator = bigint(1); + auto radixPoint = find(_literal.value().begin(), _literal.value().end(), '.'); + if (radixPoint != _literal.value().end()) + { + auto leadingZeroes = find_if_not( + radixPoint + 1, + _literal.value().end(), + [](char const& a) { return a == '0'; } + ); + auto fractionalBegin = leadingZeroes != _literal.value().end() ? + leadingZeroes : radixPoint + 1; + //separatly grabbing the numerator, denominator for conversions + denominator = bigint(string(fractionalBegin, _literal.value().end())); + denominator /= boost::multiprecision::pow( + bigint(10), + distance(radixPoint + 1, _literal.value().end()) + ); + numerator = bigint(string(_literal.value().begin(), radixPoint)); + + m_value = numerator + denominator; + } + else + m_value = bigint(_literal.value()); switch (_literal.subDenomination()) { + case Literal::SubDenomination::None: case Literal::SubDenomination::Wei: case Literal::SubDenomination::Second: - case Literal::SubDenomination::None: break; + } case Literal::SubDenomination::Szabo: m_value *= bigint("1000000000000"); break; @@ -385,20 +556,79 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal) } } -bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const +bool ConstantNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (auto targetType = dynamic_cast<IntegerType const*>(&_convertTo)) + if (_convertTo.category() == Category::Integer) + { + auto targetType = dynamic_cast<IntegerType const*>(&_convertTo); + if (m_value == 0) + return true; + int forSignBit = (targetType->isSigned() ? 1 : 0); + if (m_scalingFactor == 0) //if current type is integer + { + if (m_value > 0) + { + if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + return true; + } + else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit))) + return true; + return false; + } + else if (m_scalingFactor != 0) //if current type is fixed point + { + if (m_value > 0) + { + if (leftOfRadix() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + return true; + } + else if (targetType->isSigned() && -leftOfRadix() <= (u256(1) << (targetType->numBits() - forSignBit))) + return true; + return false; + } + } + else if (_convertTo.category() == Category::FixedPoint) { + auto targetType = dynamic_cast<FixedPointType const*>(&_convertTo); if (m_value == 0) return true; int forSignBit = (targetType->isSigned() ? 1 : 0); - if (m_value > 0) + if (m_scalingFactor == 0) //if the current type is an integer, focus on the integer bits { - if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + if (m_value > 0) + { + if (m_value <= (u256(-1) >> (256 - targetType->integerBits() + forSignBit))) + return true; + } + else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->integerBits() - forSignBit))) + return true; + return false; + } + else if (m_scalingFactor != 0) //if the current type is fixed point, focus on both the + { //integer bits and fractional bits and ensure they fit + if (m_value > 0) + { + if ( + leftOfRadix() <= (u256(-1) >> (256 - targetType->integerBits() + forSignBit)) && + rightOfRadix() <= (u256(-1) >> (256 - targetType->fractionalBits() + forSignBit)) + ) + return true; + } + else if ( + targetType->isSigned() && + -leftOfRadix() <= (u256(1) >> (256 - targetType->integerBits() + forSignBit)) && + -rightOfRadix() <= (u256(1) >> (256 - targetType->fractionalBits() + forSignBit)) + ) return true; + return false; } - else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit))) + } + else if (_convertTo.category() == Category::FixedPoint) + { + cout << "IMPLICIT CONVERSION" << endl; + if (fixedPointType() && fixedPointType()->isImplicitlyConvertibleTo(_convertTo)) return true; + return false; } else if (_convertTo.category() == Category::FixedBytes) @@ -406,23 +636,29 @@ bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) cons FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo); return fixedBytes.numBytes() * 8 >= integerType()->numBits(); } - else - return false; + return false; } -bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const +bool ConstantNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - TypePointer intType = integerType(); - return intType && intType->isExplicitlyConvertibleTo(_convertTo); + if (m_value.denominator() == 1) + { + TypePointer intType = integerType(); + return intType && intType->isExplicitlyConvertibleTo(_convertTo); + } + TypePointer fixType = fixedPointType(); + return fixType && fixType->isExplicitlyConvertibleTo(_convertTo); } -TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const +TypePointer ConstantNumberType::unaryOperatorResult(Token::Value _operator) const { - bigint value; + rational value; switch (_operator) { case Token::BitNot: - value = ~m_value; + if(m_value.denominator() != 1) + BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + value = ~m_value.numerator(); break; case Token::Add: value = m_value; @@ -435,10 +671,10 @@ TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) con default: return TypePointer(); } - return make_shared<IntegerConstantType>(value); + return make_shared<ConstantNumberType>(value); } -TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { if (_other->category() == Category::Integer) { @@ -447,106 +683,176 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty return TypePointer(); return intType->binaryOperatorResult(_operator, _other); } + else if (_other->category() == Category::FixedPoint) + { + cout << "BINARY OPERATOR RESULTS" << endl; + shared_ptr<FixedPointType const> fixType = fixedPointType(); + if (!fixType) + return TypePointer(); + return fixType->binaryOperatorResult(_operator, _other); + } else if (_other->category() != category()) return TypePointer(); - IntegerConstantType const& other = dynamic_cast<IntegerConstantType const&>(*_other); + ConstantNumberType const& other = dynamic_cast<ConstantNumberType const&>(*_other); if (Token::isCompareOp(_operator)) { - shared_ptr<IntegerType const> thisIntegerType = integerType(); - shared_ptr<IntegerType const> otherIntegerType = other.integerType(); - if (!thisIntegerType || !otherIntegerType) - return TypePointer(); - return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + if (m_value.denominator() == 1) + { + shared_ptr<IntegerType const> thisIntegerType = integerType(); + shared_ptr<IntegerType const> otherIntegerType = other.integerType(); + if (!thisIntegerType || !otherIntegerType) + return TypePointer(); + return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + } + else + { + cout << "BINARY OPERATOR RESULTS PART 2" << endl; + shared_ptr<FixedPointType const> thisFixedPointType = fixedPointType(); + shared_ptr<FixedPointType const> otherFixedPointType = other.fixedPointType(); + if (!thisFixedPointType || !otherFixedPointType) + return TypePointer(); + return thisFixedPointType->binaryOperatorResult(_operator, otherFixedPointType); + } } else { - bigint value; + rational value; + bool fixedPointType = (m_value.denominator() != 1 || other.m_value.denominator() != 1); switch (_operator) { + //bit operations will only be enabled for integers and fixed types that resemble integers case Token::BitOr: - value = m_value | other.m_value; + if (fixedPointType) + BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + value = m_value.numerator() | other.m_value.numerator(); break; case Token::BitXor: - value = m_value ^ other.m_value; + if (fixedPointType) + BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + value = m_value.numerator() ^ other.m_value.numerator(); break; case Token::BitAnd: - value = m_value & other.m_value; + if (fixedPointType) + BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + value = m_value.numerator() & other.m_value.numerator(); break; case Token::Add: - value = m_value + other.m_value; + value = leftOfRadix() + other.leftOfRadix(); + value *= boost::multiprecision::pow(bigint(10), scale); + value += rightOfRadix() + other.rightOfRadix();; break; case Token::Sub: - value = m_value - other.m_value; + value = leftOfRadix() - other.leftOfRadix(); + if (rightOfRadix() < other.rightOfRadix()) + scale = other.m_scalingFactor; + value *= boost::multiprecision::pow(bigint(10), scale); + value += (rightOfRadix() - other.rightOfRadix()); break; + //these next 4 need to be scaled accordingly if it's a fixed type case Token::Mul: + scale = m_scalingFactor - other.m_scalingFactor; value = m_value * other.m_value; - break; + break; case Token::Div: if (other.m_value == 0) return TypePointer(); - value = m_value / other.m_value; + else + value = m_value / other.m_value; break; case Token::Mod: + { if (other.m_value == 0) return TypePointer(); - value = m_value % other.m_value; - break; + else if (fixedPointType) + { + value = m_value; + bigint integers = m_value.numerator() / m_value.denominator(); + value -= integers; + } + else + value = m_value.numerator() % other.m_value.numerator(); + break; case Token::Exp: - if (other.m_value < 0) + { + bigint newDenominator; + bigint newNumerator; + if (other.m_value.denominator() != 1) return TypePointer(); - else if (other.m_value > numeric_limits<unsigned int>::max()) + else if (abs(other.m_value) > numeric_limits<unsigned int>::max()) return TypePointer(); + else if (other.m_value < 0) //apply inverse + { + rational absoluteValue = abs(other.m_value); + newDenominator = boost::multiprecision::pow(m_value.numerator(), absoluteValue.numerator().convert_to<unsigned int>()); + newNumerator = boost::multiprecision::pow(m_value.denominator(), absoluteValue.numerator().convert_to<unsigned int>()); + value = rational(newNumerator, newDenominator); + } else - value = boost::multiprecision::pow(m_value, other.m_value.convert_to<unsigned int>()); + { + newNumerator = boost::multiprecision::pow(m_value.numerator(), other.m_value.numerator().convert_to<unsigned int>()); + newDenominator = boost::multiprecision::pow(m_value.denominator(), other.m_value.numerator().convert_to<unsigned int>()); + value = rational(newNumerator, newDenominator); + } break; + } default: return TypePointer(); } - return make_shared<IntegerConstantType>(value); + return make_shared<ConstantNumberType>(value); } } -bool IntegerConstantType::operator==(Type const& _other) const +bool ConstantNumberType::operator==(Type const& _other) const { if (_other.category() != category()) return false; - return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value; + ConstantNumberType const& other = dynamic_cast<ConstantNumberType const&>(_other); + return m_value == other.m_value; } -string IntegerConstantType::toString(bool) const +string ConstantNumberType::toString(bool) const { - return "int_const " + m_value.str(); + if (m_value.denominator() == 1) + return "int_const " + m_value.numerator().str(); + + return "rational_const " + m_value.numerator().str() + '/' + m_value.denominator().str(); } -u256 IntegerConstantType::literalValue(Literal const*) const +u256 ConstantNumberType::literalValue(Literal const*) const { u256 value; // we ignore the literal and hope that the type was correctly determined - solAssert(m_value <= u256(-1), "Integer constant too large."); - solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small."); + solAssert(m_value <= u256(-1), "Number constant too large."); + solAssert(m_value >= -(bigint(1) << 255), "Number constant too small."); if (m_value >= 0) - value = u256(m_value); + value = u256(m_value.numerator()); else - value = s2u(s256(m_value)); + value = s2u(s256(m_value.numerator())); return value; } -TypePointer IntegerConstantType::mobileType() const +TypePointer ConstantNumberType::mobileType() const { - auto intType = integerType(); - solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false)); - return intType; + if (m_value.denominator() == 1) + { + auto intType = integerType(); + solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false)); + return intType; + } + auto fixType = fixedPointType(); + solAssert(!!fixType, "mobileType called with invalid fixed constant " + toString(false)); + return fixType; } -shared_ptr<IntegerType const> IntegerConstantType::integerType() const +shared_ptr<IntegerType const> ConstantNumberType::integerType() const { - bigint value = m_value; + bigint value = m_value.numerator() / m_value.denominator(); bool negative = (value < 0); if (negative) // convert to positive number of same bit requirements - value = ((-value) - 1) << 1; + value = ((0 - value) - 1) << 1; if (value > u256(-1)) return shared_ptr<IntegerType const>(); else @@ -556,6 +862,32 @@ shared_ptr<IntegerType const> IntegerConstantType::integerType() const ); } +shared_ptr<FixedPointType const> ConstantNumberType::fixedPointType() const +{ + rational value = m_value; + cout << "Original value: " << value << endl; + bool negative = (value < 0); + if (negative) // convert to absolute value + value = abs(value); + if (value > u256(-1)) + return shared_ptr<FixedPointType const>(); + else + { + // need to fix this because these aren't the proper M and N + bigint integerBits = m_value.numerator() / m_value.denominator(); + bigint remains = m_value.numerator() % m_value.denominator(); + cout << "Integer: " << integerBits.str() << endl; + cout << "Remains: " << remains.str() << endl << endl; + bigint fractionalBits; + return make_shared<FixedPointType>( + max(bytesRequired(integerBits), 1u) * 8, max(bytesRequired(fractionalBits), 1u) * 8, + negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned + ); + } +} + + + StringLiteralType::StringLiteralType(Literal const& _literal): m_value(_literal.value()) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index d42bb5dd..e0beabf8 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -26,6 +26,7 @@ #include <string> #include <map> #include <boost/noncopyable.hpp> +#include <boost/rational.hpp> #include <libdevcore/Common.h> #include <libdevcore/CommonIO.h> #include <libsolidity/interface/Exceptions.h> @@ -43,6 +44,7 @@ class FunctionType; // forward using TypePointer = std::shared_ptr<Type const>; using FunctionTypePointer = std::shared_ptr<FunctionType const>; using TypePointers = std::vector<TypePointer>; +using rational = boost::rational<bigint>; enum class DataLocation { Storage, CallData, Memory }; @@ -133,7 +135,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type public: enum class Category { - Integer, IntegerConstant, StringLiteral, Bool, Real, Array, + Integer, NumberConstant, StringLiteral, Bool, FixedPoint, Array, FixedBytes, Contract, Struct, Function, Enum, Tuple, Mapping, TypeType, Modifier, Magic, Module }; @@ -202,7 +204,7 @@ public: virtual bool isValueType() const { return false; } virtual unsigned sizeOnStack() const { return 1; } /// @returns the mobile (in contrast to static) type corresponding to the given type. - /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type + /// This returns the corresponding integer type for ConstantTypes and the pointer type /// for storage reference types. virtual TypePointer mobileType() const { return shared_from_this(); } /// @returns true if this is a non-value type and the data of this type is stored at the @@ -309,20 +311,64 @@ private: }; /** - * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10. + * A fixed point type number (signed, unsigned). + */ +class FixedPointType: public Type +{ +public: + enum class Modifier + { + Unsigned, Signed + }; + virtual Category category() const override { return Category::FixedPoint; } + + explicit FixedPointType(int _integerBits, int _fractionalBits, Modifier _modifier = Modifier::Unsigned); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual bool operator==(Type const& _other) const override; + + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : (m_integerBits + m_fractionalBits) / 8; } + virtual unsigned storageBytes() const override { return (m_integerBits + m_fractionalBits) / 8; } + virtual bool isValueType() const override { return true; } + + virtual std::string toString(bool _short) const override; + + virtual TypePointer encodingType() const override { return shared_from_this(); } + virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + + int numBits() const { return m_integerBits + m_fractionalBits; } + int integerBits() const { return m_integerBits; } + int fractionalBits() const { return m_fractionalBits; } + bool isSigned() const { return m_modifier == Modifier::Signed; } + +private: + int m_integerBits; + int m_fractionalBits; + Modifier m_modifier; +}; + +/** + * Integer and fixed point constants either literals or computed. + * Example expressions: 2, 3.14, 2+10.2, ~10. * There is one distinct type per value. */ -class IntegerConstantType: public Type +class ConstantNumberType: public Type { public: - virtual Category category() const override { return Category::IntegerConstant; } + + virtual Category category() const override { return Category::NumberConstant; } /// @returns true if the literal is a valid integer. static bool isValidLiteral(Literal const& _literal); - explicit IntegerConstantType(Literal const& _literal); - explicit IntegerConstantType(bigint _value): m_value(_value) {} - + explicit ConstantNumberType(Literal const& _literal); + explicit ConstantNumberType(rational _value): + m_value(_value) + {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -339,9 +385,12 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr<IntegerType const> integerType() const; - + /// @returns the smallest fixed type that can hold the value or an empty pointer + std::shared_ptr<FixedPointType const> fixedPointType() const; + bigint denominator() const { return m_value.denominator(); } + private: - bigint m_value; + rational m_value; }; /** diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 36ed480e..a2c44cd3 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -345,10 +345,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp break; case Type::Category::Integer: case Type::Category::Contract: - case Type::Category::IntegerConstant: + case Type::Category::NumberConstant: + case Type::Category::FixedPoint: if (targetTypeCategory == Type::Category::FixedBytes) { - solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant, + solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::NumberConstant, "Invalid conversion to FixedBytesType requested."); // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment @@ -361,15 +362,31 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp else if (targetTypeCategory == Type::Category::Enum) // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); + else if (targetTypeCategory == Type::Category::FixedPoint) + { + solAssert( + stackTypeCategory == Type::Category::Integer || + stackTypeCategory == Type::Category::NumberConstant || + stackTypeCategory == Type::Category::FixedPoint, + "Invalid conversion to FixedMxNType requested." + ); + //shift all integer bits onto the left side of the fixed type + FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType); + if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack)) + if (targetFixedPointType.integerBits() > typeOnStack->numBits()) + cleanHigherOrderBits(*typeOnStack); + //need m_context call here + + } else { solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); IntegerType addressType(0, IntegerType::Modifier::Address); IntegerType const& targetType = targetTypeCategory == Type::Category::Integer ? dynamic_cast<IntegerType const&>(_targetType) : addressType; - if (stackTypeCategory == Type::Category::IntegerConstant) + if (stackTypeCategory == Type::Category::NumberConstant) { - IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack); + ConstantNumberType const& constType = dynamic_cast<ConstantNumberType const&>(_typeOnStack); // We know that the stack is clean, we only have to clean for a narrowing conversion // where cleanup is forced. if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded) @@ -419,6 +436,19 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp ); break; } + /*case Type::Category::Fixed: + { + if (targetTypeCategory == Type::Category::Integer) + { + //need some guidance here + } + else if (targetTypeCategory == Type::Category::FixedBytes) + { + //need some guidance here + } + else + //need some guidance here + }*/ case Type::Category::Array: { solAssert(targetTypeCategory == stackTypeCategory, ""); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index ab02e0b5..14bc0855 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -285,7 +285,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) // the operator should know how to convert itself and to which types it applies, so // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that // represents the operator - if (_unaryOperation.annotation().type->category() == Type::Category::IntegerConstant) + if (_unaryOperation.annotation().type->category() == Type::Category::NumberConstant) { m_context << _unaryOperation.annotation().type->literalValue(nullptr); return false; @@ -360,7 +360,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); - else if (commonType.category() == Type::Category::IntegerConstant) + else if (commonType.category() == Type::Category::NumberConstant) m_context << commonType.literalValue(nullptr); else { @@ -370,7 +370,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) // for commutative operators, push the literal as late as possible to allow improved optimization auto isLiteral = [](Expression const& _e) { - return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::IntegerConstant; + return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::NumberConstant; }; bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) @@ -1225,7 +1225,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) switch (type->category()) { - case Type::Category::IntegerConstant: + case Type::Category::NumberConstant: case Type::Category::Bool: m_context << type->literalValue(&_literal); break; diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index fcadd2ff..7d9fa4c8 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -186,6 +186,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const dynamic_cast<IntegerType const&>(*m_dataType).isSigned() ) m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND; + //need something here for Fixed...guidance would be nice else m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND; } @@ -240,6 +241,9 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc << Instruction::MUL << Instruction::DIV; m_context << Instruction::MUL << Instruction::OR; + //else if (m_dataType->category() == Type::Category::Fixed) + //trying to figure out what this does...going to require some more assistance + m_context << Instruction::MUL << eth::Instruction::OR; // stack: value storage_ref updated_value m_context << Instruction::SWAP1 << Instruction::SSTORE; if (_move) diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index 24fbab13..e6f759aa 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -428,8 +428,11 @@ bool Why3Translator::visit(BinaryOperation const& _binaryOperation) Type const& commonType = *_binaryOperation.annotation().commonType; Token::Value const c_op = _binaryOperation.getOperator(); - if (commonType.category() == Type::Category::IntegerConstant) + if (commonType.category() == Type::Category::NumberConstant) { + auto const& constantNumber = dynamic_cast<ConstantNumberType const&>(commonType); + if (constantNumber.denominator() != bigint(1)) + error(_binaryOperation, "Fractional numbers not supported."); add("(of_int " + toString(commonType.literalValue(nullptr)) + ")"); return false; } @@ -589,9 +592,14 @@ bool Why3Translator::visit(Literal const& _literal) else add("true"); break; - case Type::Category::IntegerConstant: + case Type::Category::NumberConstant: + { + auto const& constantNumber = dynamic_cast<ConstantNumberType const&>(*type); + if (constantNumber.denominator() != 1) + error(_literal, "Fractional numbers not supported."); add("(of_int " + toString(type->literalValue(&_literal)) + ")"); break; + } default: error(_literal, "Not supported."); } diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index c73368e5..ef817d5d 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -109,6 +109,7 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; + int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) { try @@ -121,6 +122,7 @@ int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) return -1; } } + tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); @@ -171,6 +173,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s } return make_tuple(Token::Identifier, 0, 0); } + return make_tuple(keywordByName(_literal), 0, 0); } Token::Value Token::keywordByName(string const& _name) |