From 9a075458ad104921c9d747cd34d3e5c299638406 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Thu, 18 Feb 2016 16:39:11 -0600 Subject: initial work for fixed types...potentially needing a constant literal type for this notation Rational implemented...trying to figure out exponential fix for token bug, also quick fix for the wei and seconds fixed problem with var...probably a conversion problem for fixed in size capabilities adding fixed type tests Removing bitshift and regrouping fixed type tests together size capabilities functioning properly for fixed types got exponents up and working with their inverse, changed a few of the tests....something is working that likely shouldn't be slight changes to how to flip the rational negative around...still trying to figure it out tests added updated tests odd differences in trying soltest from solc binary, let me know if you can replicate test not working for odd reason fixed test problem with fixed literals...still need a way to log this error broken up the tests, added some, changed some things in types and began compiler work moar tests and prepping for rebuilding much of the types.cpp file further fixing initial work for fixed types...potentially needing a constant literal type for this --- libsolidity/analysis/ConstantEvaluator.cpp | 6 +- libsolidity/analysis/ReferencesResolver.cpp | 5 +- libsolidity/analysis/TypeChecker.cpp | 22 +- libsolidity/ast/Types.cpp | 472 ++++++++++++++++++--- libsolidity/ast/Types.h | 69 ++- libsolidity/codegen/CompilerUtils.cpp | 38 +- libsolidity/codegen/ExpressionCompiler.cpp | 8 +- libsolidity/codegen/LValue.cpp | 4 + libsolidity/formal/Why3Translator.cpp | 12 +- libsolidity/parsing/Token.cpp | 3 + test/libsolidity/SolidityEndToEndTest.cpp | 23 + test/libsolidity/SolidityNameAndTypeResolution.cpp | 437 +++++++++++++++++-- test/libsolidity/SolidityParser.cpp | 46 ++ 13 files changed, 1010 insertions(+), 135 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(subType.get())) + if (!dynamic_cast(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(leftType.get())) + if (!dynamic_cast(leftType.get())) BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression.")); - if (!dynamic_cast(rightType.get())) + if (!dynamic_cast(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(length->annotation().type.get()); + auto const* lengthType = dynamic_cast(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(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(valueComponentType)->integerType() + valueComponentType->category() == Type::Category::NumberConstant && + !dynamic_pointer_cast(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(type(_statement.expression()))->integerType()) + if (type(_statement.expression())->category() == Type::Category::NumberConstant) + if (!dynamic_pointer_cast(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(argType.get())) + if (auto t = dynamic_cast(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(type(*index).get())) - if (!actualType.isDynamicallySized() && actualType.length() <= integerType->literalValue(nullptr)) + if (auto numberType = dynamic_cast(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(type(*index).get())) + if (auto length = dynamic_cast(type(*index).get())) resultType = make_shared(make_shared( DataLocation::Memory, typeType.actualType(), @@ -1387,7 +1391,7 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto integerType = dynamic_cast(type(*index).get())) + if (auto integerType = dynamic_cast(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; 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(m, IntegerType::Modifier::Unsigned); case Token::BytesM: return make_shared(m); + case Token::FixedMxN: + return make_shared(m, n, FixedPointType::Modifier::Signed); + case Token::UFixedMxN: + return make_shared(m, n, FixedPointType::Modifier::Unsigned); case Token::Int: return make_shared(256, IntegerType::Modifier::Signed); case Token::UInt: return make_shared(256, IntegerType::Modifier::Unsigned); + case Token::Fixed: + return make_shared(128, 128, FixedPointType::Modifier::Signed); + case Token::UFixed: + return make_shared(128, 128, FixedPointType::Modifier::Unsigned); case Token::Byte: return make_shared(1); case Token::Address: @@ -171,9 +181,10 @@ TypePointer Type::forLiteral(Literal const& _literal) case Token::FalseLiteral: return make_shared(); case Token::Number: - if (!IntegerConstantType::isValidLiteral(_literal)) - return TypePointer(); - return make_shared(_literal); + if (ConstantNumberType::isValidLiteral(_literal)) + return make_shared(_literal); + else + return TypePointer(); case Token::StringLiteral: return make_shared(_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(_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(_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(_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(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(_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(_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(); + // 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(_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(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(&_convertTo)) + if (_convertTo.category() == Category::Integer) + { + auto targetType = dynamic_cast(&_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(&_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(_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(value); + return make_shared(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 fixType = fixedPointType(); + if (!fixType) + return TypePointer(); + return fixType->binaryOperatorResult(_operator, _other); + } else if (_other->category() != category()) return TypePointer(); - IntegerConstantType const& other = dynamic_cast(*_other); + ConstantNumberType const& other = dynamic_cast(*_other); if (Token::isCompareOp(_operator)) { - shared_ptr thisIntegerType = integerType(); - shared_ptr otherIntegerType = other.integerType(); - if (!thisIntegerType || !otherIntegerType) - return TypePointer(); - return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + if (m_value.denominator() == 1) + { + shared_ptr thisIntegerType = integerType(); + shared_ptr otherIntegerType = other.integerType(); + if (!thisIntegerType || !otherIntegerType) + return TypePointer(); + return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + } + else + { + cout << "BINARY OPERATOR RESULTS PART 2" << endl; + shared_ptr thisFixedPointType = fixedPointType(); + shared_ptr 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::max()) + else if (abs(other.m_value) > numeric_limits::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()); + newNumerator = boost::multiprecision::pow(m_value.denominator(), absoluteValue.numerator().convert_to()); + value = rational(newNumerator, newDenominator); + } else - value = boost::multiprecision::pow(m_value, other.m_value.convert_to()); + { + newNumerator = boost::multiprecision::pow(m_value.numerator(), other.m_value.numerator().convert_to()); + newDenominator = boost::multiprecision::pow(m_value.denominator(), other.m_value.numerator().convert_to()); + value = rational(newNumerator, newDenominator); + } break; + } default: return TypePointer(); } - return make_shared(value); + return make_shared(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(_other).m_value; + ConstantNumberType const& other = dynamic_cast(_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 IntegerConstantType::integerType() const +shared_ptr 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(); else @@ -556,6 +862,32 @@ shared_ptr IntegerConstantType::integerType() const ); } +shared_ptr 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(); + 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( + 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 #include #include +#include #include #include #include @@ -43,6 +44,7 @@ class FunctionType; // forward using TypePointer = std::shared_ptr; using FunctionTypePointer = std::shared_ptr; using TypePointers = std::vector; +using rational = boost::rational; enum class DataLocation { Storage, CallData, Memory }; @@ -133,7 +135,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this integerType() const; - + /// @returns the smallest fixed type that can hold the value or an empty pointer + std::shared_ptr 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(_targetType); + if (auto typeOnStack = dynamic_cast(&_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(_targetType) : addressType; - if (stackTypeCategory == Type::Category::IntegerConstant) + if (stackTypeCategory == Type::Category::NumberConstant) { - IntegerConstantType const& constType = dynamic_cast(_typeOnStack); + ConstantNumberType const& constType = dynamic_cast(_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(&_e) || _e.annotation().type->category() == Type::Category::IntegerConstant; + return dynamic_cast(&_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(*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(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(*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::fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); @@ -171,6 +173,7 @@ tuple Token::fromIdentifierOrKeyword(s } return make_tuple(Token::Identifier, 0, 0); } + return make_tuple(keywordByName(_literal), 0, 0); } Token::Value Token::keywordByName(string const& _name) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 9df64cdc..c3bac3d7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6631,6 +6631,29 @@ BOOST_AUTO_TEST_CASE(delete_on_array_of_structs) // This code interprets x as an array length and thus will go out of gas. // neither of the two should throw due to out-of-bounds access BOOST_CHECK(callContractFunction("f()") == encodeArgs(true)); + +} + +BOOST_AUTO_TEST_CASE(fixed_data_type) +{ + char const* sourceCode = R"( + contract C { + fixed public pi = 3.141592; + } + )"; + compileAndRun(sourceCode, 0, "C"); +} + +BOOST_AUTO_TEST_CASE(fixed_data_type_expression) +{ + char const* sourceCode = R"( + contract C { + function f(fixed a) returns (fixed) { + return (a + 3); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); } BOOST_AUTO_TEST_CASE(internal_library_function) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9c411781..b3679305 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1330,15 +1330,6 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError); } -BOOST_AUTO_TEST_CASE(exp_operator_negative_exponent) -{ - char const* sourceCode = R"( - contract test { - function f() returns(uint d) { return 2 ** -3; } - })"; - BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError); -} - BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) { char const* sourceCode = R"( @@ -2229,18 +2220,6 @@ BOOST_AUTO_TEST_CASE(literal_strings) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(invalid_integer_literal_fraction) -{ - char const* text = R"( - contract Foo { - function f() { - var x = 1.20; - } - } - )"; - BOOST_CHECK(expectError(text) == Error::Type::TypeError); -} - BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp) { char const* text = R"( @@ -2792,8 +2771,8 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion) uint8 x = 7; uint16 y = 8; uint32 z = 9; - uint32[3] memory ending = [x, y, z]; - return (ending[1]); + uint32[3] memory ending = [x, y, z]; + return (ending[1]); } } )"; @@ -3230,27 +3209,45 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(invalid_fixed_types) +BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) { char const* text = R"( + library L { function l() {} } contract test { function f() { - fixed0x7 a = .3; - fixed99999999999999999999999999999999999999x7 b = 9.5; + L.l.value; } } )"; BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) + +BOOST_AUTO_TEST_CASE(invalid_fixed_types_0x7_mxn) { char const* text = R"( - library L { function l() {} } contract test { - function f() { - L.l.value; - } + fixed0x7 a = .3; + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_fixed_types_long_invalid_identifier) +{ + char const* text = R"( + contract test { + fixed99999999999999999999999999999999999999x7 b = 9.5; + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_fixed_types_7x8_mxn) +{ + char const* text = R"( + contract test { + fixed7x8 c = 3.12345678; } )"; BOOST_CHECK(!success(text)); @@ -3282,6 +3279,384 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) BOOST_CHECK(!success(text)); } +BOOST_AUTO_TEST_CASE(valid_fixed_types) +{ + char const* text = R"( + contract test { + function f(){ + fixed8x8 a = 87654321.12345678; + fixed16x16 b = a**2; + fixed24x24 c = b**3; + fixed32x32 d = b**2; + fixed40x40 e = a**5; + } + } + )"; + + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) +{ + char const* text = R"( + contract test { + function f() { + uint128 a = 3; + int128 b = 4; + fixed c = b; + ufixed d = a; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_const_int_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed c = 3; + ufixed d = 4; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_literal) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3.14; + ufixed d = 2.555555; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3.14 * 3; + ufixed b = 4 - 2.555555; + fixed c = 1.0 / 3.0; + ufixed d = 599 + .5367; + ufixed e = 35.245 % 12.9; + ufixed f = 1.2 % 2.00000; + fixed g = 2 ** -2; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) +{ + char const* text = R"( + contract test { + function f() { + uint[fixed(3.56)] a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + + +BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) +{ + char const* text = R"( + contract test { + function f() { + uint[3.56] a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) +{ + char const* text = R"( + contract test { + mapping(fixed => string) fixedString; + function f() { + fixedString[3.14] = "Pi"; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(inline_array_fixed_type) +{ + char const* text = R"( + contract test { + function f() { + fixed[3] memory a = [fixed(3.5), fixed(4.1234), fixed(967.32)]; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) +{ + char const* text = R"( + contract test { + function f() { + ufixed8x16[3] memory a = [3.5, 4.1234, 2.5]; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(zero_and_eight_variants_fixed) +{ + char const* text = R"( + contract A { + fixed8x0 someInt = 4; + fixed0x8 half = 0.5; + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) +{ + char const* text = R"( + contract test { + function f() { + ufixed0x8 a = 0.12345678; + ufixed8x0 b = 12345678.0; + ufixed0x8 c = 0.00000009; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) +{ + char const* text = R"( + contract test { + function f() { + var a = 0.12345678; + var b = 12345678.0; + var c = 0.00000009; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_rational_exponent_usage) +{ + char const* text = R"( + contract test { + function f() { + fixed8x8 a = 3 ** 1.5; + fixed24x24 b = 2 ** (1/2); + fixed40x40 c = 42 ** (-1/4); + fixed48x48 d = 16 ** -0.33; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3 ** fixed(1.5); + fixed b = 2 ** fixed(1/2); + fixed c = 42 ** fixed(-1/4); + fixed d = 16 ** fixed(-0.33); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_unary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = +3.5134; + fixed b = -2.5145; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = ~3.56; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 | 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 ^ 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 & 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) +{ + char const* text = R"( + contract test { + function f() { + uint128 a = 3; + int128 b = 4; + fixed c = b; + ufixed d = a; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_const_int_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed c = 3; + ufixed d = 4; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_literal) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3.14; + ufixed d = 2.555555; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3.14 * 3; + ufixed b = 4 - 2.555555; + fixed c = 1.0 / 3.0; + ufixed d = 599 + .5367; + ufixed e = 35.245 % 12.9; + ufixed g = 1.2 % 2.00000; + //ufixed f = 2.222 ** 3.333; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_literal_seconds_and_wei) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3.14 wei; + ufixed b = 4.5 seconds; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) +{ + char const* text = R"( + contract test { + function f() { + uint[3.56] a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) +{ + char const* text = R"( + contract test { + function f() { + fixed[3] memory a = [3.5, 4.1234, 967.32]; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) +{ + char const* text = R"( + contract test { + function f() { + fixed0x8 a = 0.12345678; + fixed8x0 b = 12345678.0; + fixed0x8 c = 0.00000009; + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index e43b026c..909d18c9 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1177,6 +1177,52 @@ BOOST_AUTO_TEST_CASE(conditional_with_assignment) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables) +{ + char const* text = R"( + contract A { + fixed40x40 storeMe; + function f(ufixed x, fixed32x32 y) { + ufixed8x8 a; + fixed b; + } + } + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(declaring_fixed_literal_variables) +{ + char const* text = R"( + contract A { + fixed40x40 pi = 3.14; + } + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(no_double_radix_in_fixed_literal) +{ + char const* text = R"( + contract A { + fixed40x40 pi = 3.14.15; + } + )"; + BOOST_CHECK(!successParse(text)); +} + +BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.0x2; + } + } + )"; + BOOST_CHECK(!successParse(text)); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From 6fa5e0fac93e9eb584daa2c7b637ca37dabc0356 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Thu, 3 Mar 2016 12:48:42 -0600 Subject: Rational implemented...trying to figure out exponential --- libsolidity/ast/Types.cpp | 67 +++++----------------- test/libsolidity/SolidityNameAndTypeResolution.cpp | 32 ++++++++++- 2 files changed, 45 insertions(+), 54 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index b1a0f4e6..577838fb 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -521,7 +521,6 @@ ConstantNumberType::ConstantNumberType(Literal const& _literal) } else m_value = bigint(_literal.value()); - switch (_literal.subDenomination()) { case Literal::SubDenomination::None: @@ -564,28 +563,14 @@ bool ConstantNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const 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 (m_value > 0) - { - if (leftOfRadix() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) - return true; - } - else if (targetType->isSigned() && -leftOfRadix() <= (u256(1) << (targetType->numBits() - forSignBit))) + if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) return true; - return false; } + else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit))) + return true; + return false; } else if (_convertTo.category() == Category::FixedPoint) { @@ -593,35 +578,22 @@ bool ConstantNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const if (m_value == 0) return true; int forSignBit = (targetType->isSigned() ? 1 : 0); - if (m_scalingFactor == 0) //if the current type is an integer, focus on the integer bits + if (m_value > 0) { - 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))) + if ( + m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)) && + m_scalingFactor <= targetType->fractionalBits() + ) 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)) + -m_value <= (u256(1) >> (256 - targetType->numBits() + forSignBit)) && + m_scalingFactor <= targetType->fractionalBits() ) return true; return false; } + } else if (_convertTo.category() == Category::FixedPoint) { @@ -738,18 +710,11 @@ TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, Typ value = m_value.numerator() & other.m_value.numerator(); break; case Token::Add: - value = leftOfRadix() + other.leftOfRadix(); - value *= boost::multiprecision::pow(bigint(10), scale); - value += rightOfRadix() + other.rightOfRadix();; + value = m_value + other.m_value; break; case Token::Sub: - value = leftOfRadix() - other.leftOfRadix(); - if (rightOfRadix() < other.rightOfRadix()) - scale = other.m_scalingFactor; - value *= boost::multiprecision::pow(bigint(10), scale); - value += (rightOfRadix() - other.rightOfRadix()); + value = m_value - other.m_value; 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; @@ -761,7 +726,6 @@ TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, Typ value = m_value / other.m_value; break; case Token::Mod: - { if (other.m_value == 0) return TypePointer(); else if (fixedPointType) @@ -815,7 +779,6 @@ string ConstantNumberType::toString(bool) const { if (m_value.denominator() == 1) return "int_const " + m_value.numerator().str(); - return "rational_const " + m_value.numerator().str() + '/' + m_value.denominator().str(); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b3679305..c078dc10 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3598,8 +3598,9 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) fixed c = 1.0 / 3.0; ufixed d = 599 + .5367; ufixed e = 35.245 % 12.9; - ufixed g = 1.2 % 2.00000; - //ufixed f = 2.222 ** 3.333; + ufixed f = 1.2 % 2.00000; + //fixed g = 2 ** -1.5; + //fixed h = -3 ** -5.8; } } )"; @@ -3631,6 +3632,19 @@ BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) BOOST_CHECK(!success(text)); } +BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) +{ + char const* text = R"( + contract test { + mapping(fixed => string) fixedString; + function f() { + fixedString[3.14] = "Pi"; + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) { char const* text = R"( @@ -3657,6 +3671,20 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(var_capable_of_holding_fixed_constants) +{ + char const* text = R"( + contract test { + function f() { + var a = 0.12345678; + var b = 12345678.0; + var c = 0.00000009; + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From dff1a26f55adc54ccfddfa9d2e87f1dab719d8ca Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Fri, 11 Mar 2016 15:27:56 -0600 Subject: fix for token bug, also quick fix for the wei and seconds --- libsolidity/parsing/Token.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index ef817d5d..d3786524 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -153,7 +153,7 @@ tuple Token::fromIdentifierOrKeyword(s positionM < positionX && positionX < _literal.end() && *positionX == 'x' && - all_of(positionX + 1, _literal.end(), ::isdigit) + all_of(positionX++, _literal.end(), ::isdigit) ) { int n = parseSize(positionX + 1, _literal.end()); if ( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c078dc10..9ead3dcd 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3617,7 +3617,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal_seconds_and_wei) } } )"; - BOOST_CHECK(success(text)); + BOOST_CHECK(!success(text)); } BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) -- cgit v1.2.3 From 91fda50922865c1dbeed34652c30ac89f5edfadf Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Fri, 11 Mar 2016 17:53:54 -0600 Subject: fixed problem with var...probably a conversion problem for fixed in size capabilities adding fixed type tests Removing bitshift and regrouping fixed type tests together --- libsolidity/ast/Types.cpp | 2 +- libsolidity/parsing/Token.cpp | 4 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 72 +++++++++++++++++++++- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 577838fb..a0915df9 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -469,7 +469,7 @@ bool ConstantNumberType::isValidLiteral(Literal const& _literal) { //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(), diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index d3786524..cbe0c0de 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -153,9 +153,9 @@ tuple Token::fromIdentifierOrKeyword(s positionM < positionX && positionX < _literal.end() && *positionX == 'x' && - all_of(positionX++, _literal.end(), ::isdigit) + all_of(++positionX, _literal.end(), ::isdigit) ) { - int n = parseSize(positionX + 1, _literal.end()); + int n = parseSize(positionX, _literal.end()); if ( 0 <= m && m <= 256 && 0 <= n && n <= 256 && diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9ead3dcd..9c41dab8 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3222,7 +3222,6 @@ BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) BOOST_CHECK(!success(text)); } - BOOST_AUTO_TEST_CASE(invalid_fixed_types_0x7_mxn) { char const* text = R"( @@ -3544,9 +3543,41 @@ BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) } } )"; + + BOOST_CHECK(success(text)); +} + + +BOOST_AUTO_TEST_CASE(invalid_non_mod_8_fixed_types) +{ + char const* text = R"( + contract test { + function f(){ + fixed8x10 a = 12345678.1234567890; + } + } + )"; + BOOST_CHECK(!success(text)); } +BOOST_AUTO_TEST_CASE(valid_fixed_types) +{ + char const* text = R"( + contract test { + function f(){ + fixed8x8 a = 87654321.12345678; + fixed16x16 b = a**2; + fixed24x24 c = b**(1.5); + fixed32x32 d = b**2; + fixed40x40 e = a**5; + } + } + )"; + + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) { char const* text = R"( @@ -3620,6 +3651,19 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal_seconds_and_wei) BOOST_CHECK(!success(text)); } +BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) +{ + char const* text = R"( + contract test { + function f() { + uint[fixed(3.56)] a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + + BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) { char const* text = R"( @@ -3645,18 +3689,41 @@ BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(inline_array_fixed_type) +{ + char const* text = R"( + contract test { + function f() { + fixed[3] memory a = [fixed(3.5), fixed(4.1234), fixed(967.32)]; + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) { char const* text = R"( contract test { function f() { - fixed[3] memory a = [3.5, 4.1234, 967.32]; + fixed[3] memory a = [3.5, 4.1234, 967.32]; } } )"; BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(zero_and_eight_variants_fixed) +{ + char const* text = R"( + contract A { + fixed8x0 someInt = 4; + fixed0x8 half = 0.5; + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) { char const* text = R"( @@ -3685,6 +3752,7 @@ BOOST_AUTO_TEST_CASE(var_capable_of_holding_fixed_constants) BOOST_CHECK(success(text)); } + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From a1a2eac5fde0cd6f5aed206958434d655f9a194f Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Fri, 18 Mar 2016 13:52:00 -0500 Subject: size capabilities functioning properly for fixed types --- libsolidity/ast/Types.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a0915df9..d5e989ac 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -580,16 +580,11 @@ bool ConstantNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const int forSignBit = (targetType->isSigned() ? 1 : 0); if (m_value > 0) { - if ( - m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)) && - m_scalingFactor <= targetType->fractionalBits() - ) + bool properlyScaledBits = m_scalingFactor <= targetType->fractionalBits() ? + true : m_scalingFactor == 1 && targetType->fractionalBits() == 0 ? true : false; + if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)) && properlyScaledBits) return true; - else if ( - targetType->isSigned() && - -m_value <= (u256(1) >> (256 - targetType->numBits() + forSignBit)) && - m_scalingFactor <= targetType->fractionalBits() - ) + else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit)) && properlyScaledBits) return true; return false; } -- cgit v1.2.3 From 93295ae8f8e92d075584d10ac21374f83c43f759 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Fri, 18 Mar 2016 15:03:26 -0500 Subject: got exponents up and working with their inverse, changed a few of the tests....something is working that likely shouldn't be slight changes to how to flip the rational negative around...still trying to figure it out tests added updated tests odd differences in trying soltest from solc binary, let me know if you can replicate test not working for odd reason fixed test problem with fixed literals...still need a way to log this error broken up the tests, added some, changed some things in types and began compiler work moar tests and prepping for rebuilding much of the types.cpp file further fixing infinite loop still happening but it's somewhere in the fixedPoint methodd fractional bits needed algo improved! Eliminated 2 errors Corrected problems with the previous commit. No infinite loops. Actually appear to have corrected an error --- libsolidity/ast/Types.cpp | 83 +++++----- libsolidity/ast/Types.h | 5 +- libsolidity/codegen/CompilerUtils.cpp | 13 -- libsolidity/parsing/Token.cpp | 4 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 172 ++++++++++++++++----- 5 files changed, 184 insertions(+), 93 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index d5e989ac..c9df0086 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -362,6 +362,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPointType::Modifier _modifier): m_integerBits(_integerBits), m_fractionalBits(_fractionalBits), m_modifier(_modifier) { + cout << "FIXED POINT CONSTRUCTOR: " << _integerBits << "x" << _fractionalBits << endl; solAssert( m_integerBits + m_fractionalBits > 0 && m_integerBits + m_fractionalBits <= 256 && @@ -469,7 +470,6 @@ bool ConstantNumberType::isValidLiteral(Literal const& _literal) { //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(), @@ -516,18 +516,17 @@ ConstantNumberType::ConstantNumberType(Literal const& _literal) 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: break; - } case Literal::SubDenomination::Szabo: m_value *= bigint("1000000000000"); break; @@ -574,28 +573,16 @@ bool ConstantNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const } else if (_convertTo.category() == Category::FixedPoint) { - auto targetType = dynamic_cast(&_convertTo); - if (m_value == 0) + cout << "IMPLICIT CONVERSION" << endl; + if (fixedPointType() && fixedPointType()->isImplicitlyConvertibleTo(_convertTo)) return true; - int forSignBit = (targetType->isSigned() ? 1 : 0); - if (m_value > 0) - { - bool properlyScaledBits = m_scalingFactor <= targetType->fractionalBits() ? - true : m_scalingFactor == 1 && targetType->fractionalBits() == 0 ? true : false; - if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)) && properlyScaledBits) - return true; - else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit)) && properlyScaledBits) - return true; - return false; - } + return false; } 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) @@ -613,6 +600,7 @@ bool ConstantNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const TypePointer intType = integerType(); return intType && intType->isExplicitlyConvertibleTo(_convertTo); } + cout << "EXPLICIT CONVERSION" << endl; TypePointer fixType = fixedPointType(); return fixType && fixType->isExplicitlyConvertibleTo(_convertTo); } @@ -628,10 +616,10 @@ TypePointer ConstantNumberType::unaryOperatorResult(Token::Value _operator) cons value = ~m_value.numerator(); break; case Token::Add: - value = m_value; + value = +(m_value); break; case Token::Sub: - value = -m_value; + value = -(m_value); break; case Token::After: return shared_from_this(); @@ -711,7 +699,6 @@ TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, Typ value = m_value - other.m_value; break; case Token::Mul: - scale = m_scalingFactor - other.m_scalingFactor; value = m_value * other.m_value; break; case Token::Div: @@ -734,6 +721,7 @@ TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, Typ break; case Token::Exp: { + cout << "Is this the source of the problem" << endl; bigint newDenominator; bigint newNumerator; if (other.m_value.denominator() != 1) @@ -781,7 +769,7 @@ 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), "Number constant too large."); + solAssert(m_value >= -(bigint(1) << 255), "Number constant too small."); if (m_value >= 0) @@ -807,7 +795,7 @@ TypePointer ConstantNumberType::mobileType() const shared_ptr ConstantNumberType::integerType() const { - bigint value = m_value.numerator() / m_value.denominator(); + bigint value = wholeNumbers(); bool negative = (value < 0); if (negative) // convert to positive number of same bit requirements value = ((0 - value) - 1) << 1; @@ -822,28 +810,51 @@ shared_ptr ConstantNumberType::integerType() const shared_ptr 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)) + //do calculations up here + bigint integers = wholeNumbers(); + //bigint _remainder = abs(m_value.numerator() % m_value.denominator()); + bool fractionalSignBit = integers == 0; //sign the fractional side or the integer side + bool negative = (m_value < 0); + //todo: change name + bigint fractionalBits = fractionalBitsNeeded(); + + if (negative && !fractionalSignBit) // convert to positive number of same bit requirements + { + integers = ((0 - integers) - 1) << 1; + fractionalBits = ((0 - fractionalBits) - 1) << 1; + } + else if (negative && fractionalSignBit) + fractionalBits = ((0 - fractionalBits) - 1) << 1; + + if (fractionalBits > u256(-1)) return shared_ptr(); 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; + unsigned totalBytesRequired = bytesRequired(fractionalBits) * 8; + unsigned integerBytesRequired = bytesRequired(integers) * 8; return make_shared( - max(bytesRequired(integerBits), 1u) * 8, max(bytesRequired(fractionalBits), 1u) * 8, + integerBytesRequired, totalBytesRequired - integerBytesRequired, negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned ); } } +//todo: change name of function +bigint ConstantNumberType::fractionalBitsNeeded() const +{ + auto value = m_value; + for (unsigned fractionalBits = 0; value < boost::multiprecision::pow(bigint(2), 256); fractionalBits += 8, value *= 10) + { + if (value.denominator() == 1) + return value.numerator()/value.denominator(); + for ( ; value.denominator() != 1 && value < boost::multiprecision::pow(bigint(2), fractionalBits); value *= 10) + if (value.denominator() == 1) + return value.numerator()/value.denominator(); + } + cout << "too big :(" << endl; + return value.numerator()/value.denominator(); +} + StringLiteralType::StringLiteralType(Literal const& _literal): diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index e0beabf8..2039c85e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -387,8 +387,11 @@ public: std::shared_ptr integerType() const; /// @returns the smallest fixed type that can hold the value or an empty pointer std::shared_ptr fixedPointType() const; + + bigint fractionalBitsNeeded() const; bigint denominator() const { return m_value.denominator(); } - + bigint wholeNumbers() const { return m_value.numerator() / m_value.denominator(); } + private: rational m_value; }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index a2c44cd3..15446978 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -436,19 +436,6 @@ 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/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index cbe0c0de..ef817d5d 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -153,9 +153,9 @@ tuple Token::fromIdentifierOrKeyword(s positionM < positionX && positionX < _literal.end() && *positionX == 'x' && - all_of(++positionX, _literal.end(), ::isdigit) + all_of(positionX + 1, _literal.end(), ::isdigit) ) { - int n = parseSize(positionX, _literal.end()); + int n = parseSize(positionX + 1, _literal.end()); if ( 0 <= m && m <= 256 && 0 <= n && n <= 256 && diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9c41dab8..42d06fd7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3354,6 +3354,32 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(fixed_type_invalid_size_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1/3; + ufixed248x8 b = a + 2.5; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_type_valid_size_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1/3; + ufixed248x8 b = ufixed248x8(a) + 2.5; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) { char const* text = R"( @@ -3366,7 +3392,6 @@ BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) BOOST_CHECK(!success(text)); } - BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) { char const* text = R"( @@ -3404,7 +3429,7 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_type) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) +BOOST_AUTO_TEST_CASE(inline_array_fixed_rationals) { char const* text = R"( contract test { @@ -3416,25 +3441,14 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(zero_and_eight_variants_fixed) -{ - char const* text = R"( - contract A { - fixed8x0 someInt = 4; - fixed0x8 half = 0.5; - } - )"; - BOOST_CHECK(success(text)); -} - BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) { char const* text = R"( contract test { function f() { - ufixed0x8 a = 0.12345678; + ufixed0x64 a = 0.12345678; ufixed8x0 b = 12345678.0; - ufixed0x8 c = 0.00000009; + ufixed0x64 c = 0.00000009; } } )"; @@ -3547,18 +3561,18 @@ BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) BOOST_CHECK(success(text)); } - -BOOST_AUTO_TEST_CASE(invalid_non_mod_8_fixed_types) +BOOST_AUTO_TEST_CASE(valid_fraction_fixed_type) { char const* text = R"( contract test { function f(){ - fixed8x10 a = 12345678.1234567890; + fixed8x8 a = (2**24)/127; + fixed0x8 b = 1/256; } } )"; - BOOST_CHECK(!success(text)); + BOOST_CHECK(success(text)); } BOOST_AUTO_TEST_CASE(valid_fixed_types) @@ -3568,7 +3582,7 @@ BOOST_AUTO_TEST_CASE(valid_fixed_types) function f(){ fixed8x8 a = 87654321.12345678; fixed16x16 b = a**2; - fixed24x24 c = b**(1.5); + fixed24x24 c = b**3; fixed32x32 d = b**2; fixed40x40 e = a**5; } @@ -3630,27 +3644,13 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) ufixed d = 599 + .5367; ufixed e = 35.245 % 12.9; ufixed f = 1.2 % 2.00000; - //fixed g = 2 ** -1.5; - //fixed h = -3 ** -5.8; + fixed g = 2 ** -2; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_literal_seconds_and_wei) -{ - char const* text = R"( - contract test { - function f() { - fixed a = 3.14 wei; - ufixed b = 4.5 seconds; - } - } - )"; - BOOST_CHECK(!success(text)); -} - BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) { char const* text = R"( @@ -3664,7 +3664,7 @@ BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) } -BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) +BOOST_AUTO_TEST_CASE(array_declaration_with_rational) { char const* text = R"( contract test { @@ -3706,7 +3706,7 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) char const* text = R"( contract test { function f() { - fixed[3] memory a = [3.5, 4.1234, 967.32]; + ufixed8x16[3] memory a = [3.5, 4.1234, 2.5]; } } )"; @@ -3729,16 +3729,16 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) char const* text = R"( contract test { function f() { - fixed0x8 a = 0.12345678; - fixed8x0 b = 12345678.0; - fixed0x8 c = 0.00000009; + ufixed0x8 a = 0.12345678; + ufixed8x0 b = 12345678.0; + ufixed0x8 c = 0.00000009; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(var_capable_of_holding_fixed_constants) +BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) { char const* text = R"( contract test { @@ -3752,6 +3752,96 @@ BOOST_AUTO_TEST_CASE(var_capable_of_holding_fixed_constants) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(invalid_rational_exponent_usage) +{ + char const* text = R"( + contract test { + function f() { + fixed8x8 a = 3 ** 1.5; + fixed24x24 b = 2 ** (1/2); + fixed40x40 c = 42 ** (-1/4); + fixed48x48 d = 16 ** -0.33; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3 ** fixed(1.5); + fixed b = 2 ** fixed(1/2); + fixed c = 42 ** fixed(-1/4); + fixed d = 16 ** fixed(-0.33); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_unary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = +3.5134; + fixed b = -2.5145; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = ~3.56; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 | 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 ^ 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 1.56 & 3; + } + } + )"; + BOOST_CHECK(!success(text)); +} BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3 From 4d283b2b304f9c9a85fb6f03346cc7c9d0054daa Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Mon, 28 Mar 2016 13:36:48 -0500 Subject: currently what we have --- libsolidity/ast/Types.cpp | 12 ++++++++---- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c9df0086..e4f2b035 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -817,7 +817,7 @@ shared_ptr ConstantNumberType::fixedPointType() const bool negative = (m_value < 0); //todo: change name bigint fractionalBits = fractionalBitsNeeded(); - + cout << "Total int: " << fractionalBits.str() << endl; if (negative && !fractionalSignBit) // convert to positive number of same bit requirements { integers = ((0 - integers) - 1) << 1; @@ -830,10 +830,14 @@ shared_ptr ConstantNumberType::fixedPointType() const return shared_ptr(); else { - unsigned totalBytesRequired = bytesRequired(fractionalBits) * 8; + cout << "m_value: " << m_value << endl; + cout << "Total int: " << fractionalBits.str() << endl; + unsigned fractionalBytesRequired = bytesRequired(fractionalBits) * 8; unsigned integerBytesRequired = bytesRequired(integers) * 8; + cout << "Fractional Bytes Required: " << fractionalBytesRequired << endl; + cout << "Integer Bytes Required: " << integerBytesRequired << endl << endl; return make_shared( - integerBytesRequired, totalBytesRequired - integerBytesRequired, + integerBytesRequired, fractionalBytesRequired, negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned ); } @@ -842,7 +846,7 @@ shared_ptr ConstantNumberType::fixedPointType() const //todo: change name of function bigint ConstantNumberType::fractionalBitsNeeded() const { - auto value = m_value; + auto value = m_value - wholeNumbers(); for (unsigned fractionalBits = 0; value < boost::multiprecision::pow(bigint(2), 256); fractionalBits += 8, value *= 10) { if (value.denominator() == 1) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 42d06fd7..90bb7065 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3434,7 +3434,7 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_rationals) char const* text = R"( contract test { function f() { - ufixed8x16[3] memory a = [3.5, 4.1234, 2.5]; + ufixed8x16[4] memory a = [3.5, 4.1234, 2.5, 4.0]; } } )"; -- cgit v1.2.3 From 4b749fc33335cee2de50e31776ddae1f73649a7b Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Tue, 29 Mar 2016 15:08:51 -0500 Subject: changed names for Rational Constants and categories --- libsolidity/analysis/TypeChecker.cpp | 16 +++++----- libsolidity/ast/Types.cpp | 50 ++++++++++++++++-------------- libsolidity/ast/Types.h | 10 +++--- libsolidity/codegen/CompilerUtils.cpp | 10 +++--- libsolidity/codegen/ExpressionCompiler.cpp | 8 ++--- libsolidity/formal/Why3Translator.cpp | 8 ++--- 6 files changed, 53 insertions(+), 49 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ad93b38c..fb0c665c 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::NumberConstant && - !dynamic_pointer_cast(valueComponentType)->integerType() + valueComponentType->category() == Type::Category::RationalNumber && + !dynamic_pointer_cast(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::NumberConstant) - if (!dynamic_pointer_cast(type(_statement.expression()))->integerType()) + if (type(_statement.expression())->category() == Type::Category::RationalNumber) + if (!dynamic_pointer_cast(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(argType.get())) + if (auto t = dynamic_cast(argType.get())) if (!t->integerType()) typeError(arguments[i]->location(), "Integer constant too large."); } @@ -1341,7 +1341,7 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto numberType = dynamic_cast(type(*index).get())) + if (auto numberType = dynamic_cast(type(*index).get())) { if (numberType->denominator() != 1) typeError(_access.location(), "Invalid type for array access."); @@ -1372,7 +1372,7 @@ bool TypeChecker::visit(IndexAccess const& _access) else { index->accept(*this); - if (auto length = dynamic_cast(type(*index).get())) + if (auto length = dynamic_cast(type(*index).get())) resultType = make_shared(make_shared( DataLocation::Memory, typeType.actualType(), @@ -1391,7 +1391,7 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto integerType = dynamic_cast(type(*index).get())) + if (auto integerType = dynamic_cast(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 e4f2b035..4c3947df 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -181,8 +181,8 @@ TypePointer Type::forLiteral(Literal const& _literal) case Token::FalseLiteral: return make_shared(); case Token::Number: - if (ConstantNumberType::isValidLiteral(_literal)) - return make_shared(_literal); + if (RationalNumberType::isValidLiteral(_literal)) + return make_shared(_literal); else return TypePointer(); case Token::StringLiteral: @@ -326,7 +326,7 @@ string IntegerType::toString(bool) const TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->category() != Category::NumberConstant && _other->category() != category()) + if (_other->category() != Category::RationalNumber && _other->category() != category()) return TypePointer(); auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); @@ -441,7 +441,7 @@ string FixedPointType::toString(bool) const TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->category() != Category::NumberConstant + if (_other->category() != Category::RationalNumber && _other->category() != category() && _other->category() != Category::Integer ) @@ -459,7 +459,7 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi return commonType; } -bool ConstantNumberType::isValidLiteral(Literal const& _literal) +bool RationalNumberType::isValidLiteral(Literal const& _literal) { try { @@ -495,7 +495,7 @@ bool ConstantNumberType::isValidLiteral(Literal const& _literal) return true; } -ConstantNumberType::ConstantNumberType(Literal const& _literal) +RationalNumberType::RationalNumberType(Literal const& _literal) { rational numerator; rational denominator = bigint(1); @@ -554,7 +554,7 @@ ConstantNumberType::ConstantNumberType(Literal const& _literal) } } -bool ConstantNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const +bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() == Category::Integer) { @@ -593,7 +593,7 @@ bool ConstantNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } -bool ConstantNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const +bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (m_value.denominator() == 1) { @@ -605,7 +605,7 @@ bool ConstantNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const return fixType && fixType->isExplicitlyConvertibleTo(_convertTo); } -TypePointer ConstantNumberType::unaryOperatorResult(Token::Value _operator) const +TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const { rational value; switch (_operator) @@ -626,10 +626,10 @@ TypePointer ConstantNumberType::unaryOperatorResult(Token::Value _operator) cons default: return TypePointer(); } - return make_shared(value); + return make_shared(value); } -TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { if (_other->category() == Category::Integer) { @@ -649,7 +649,7 @@ TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, Typ else if (_other->category() != category()) return TypePointer(); - ConstantNumberType const& other = dynamic_cast(*_other); + RationalNumberType const& other = dynamic_cast(*_other); if (Token::isCompareOp(_operator)) { if (m_value.denominator() == 1) @@ -746,26 +746,26 @@ TypePointer ConstantNumberType::binaryOperatorResult(Token::Value _operator, Typ default: return TypePointer(); } - return make_shared(value); + return make_shared(value); } } -bool ConstantNumberType::operator==(Type const& _other) const +bool RationalNumberType::operator==(Type const& _other) const { if (_other.category() != category()) return false; - ConstantNumberType const& other = dynamic_cast(_other); + RationalNumberType const& other = dynamic_cast(_other); return m_value == other.m_value; } -string ConstantNumberType::toString(bool) const +string RationalNumberType::toString(bool) const { if (m_value.denominator() == 1) return "int_const " + m_value.numerator().str(); return "rational_const " + m_value.numerator().str() + '/' + m_value.denominator().str(); } -u256 ConstantNumberType::literalValue(Literal const*) const +u256 RationalNumberType::literalValue(Literal const*) const { u256 value; // we ignore the literal and hope that the type was correctly determined @@ -780,7 +780,7 @@ u256 ConstantNumberType::literalValue(Literal const*) const return value; } -TypePointer ConstantNumberType::mobileType() const +TypePointer RationalNumberType::mobileType() const { if (m_value.denominator() == 1) { @@ -793,7 +793,7 @@ TypePointer ConstantNumberType::mobileType() const return fixType; } -shared_ptr ConstantNumberType::integerType() const +shared_ptr RationalNumberType::integerType() const { bigint value = wholeNumbers(); bool negative = (value < 0); @@ -808,7 +808,7 @@ shared_ptr ConstantNumberType::integerType() const ); } -shared_ptr ConstantNumberType::fixedPointType() const +shared_ptr RationalNumberType::fixedPointType() const { //do calculations up here bigint integers = wholeNumbers(); @@ -844,10 +844,14 @@ shared_ptr ConstantNumberType::fixedPointType() const } //todo: change name of function -bigint ConstantNumberType::fractionalBitsNeeded() const +bigint RationalNumberType::findFractionNumberAndBits(bool getWholeNumber = false) const { - auto value = m_value - wholeNumbers(); - for (unsigned fractionalBits = 0; value < boost::multiprecision::pow(bigint(2), 256); fractionalBits += 8, value *= 10) + rational value; + if (getWholeNumber) + value = m_value; + else + value = m_value - wholeNumbers(); + for (unsigned fractionalBits = 0; value < boost::multiprecision::pow(bigint(2), 256); fractionalBits += 8, value *= 256) { if (value.denominator() == 1) return value.numerator()/value.denominator(); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 2039c85e..ef0a69b3 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -135,7 +135,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this(_targetType) : addressType; - if (stackTypeCategory == Type::Category::NumberConstant) + if (stackTypeCategory == Type::Category::RationalNumber) { - ConstantNumberType const& constType = dynamic_cast(_typeOnStack); + RationalNumberType const& constType = dynamic_cast(_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) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 14bc0855..baf587f0 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::NumberConstant) + if (_unaryOperation.annotation().type->category() == Type::Category::RationalNumber) { 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::NumberConstant) + else if (commonType.category() == Type::Category::RationalNumber) 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(&_e) || _e.annotation().type->category() == Type::Category::NumberConstant; + return dynamic_cast(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; }; 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::NumberConstant: + case Type::Category::RationalNumber: case Type::Category::Bool: m_context << type->literalValue(&_literal); break; diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index e6f759aa..5921b9a4 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -428,9 +428,9 @@ bool Why3Translator::visit(BinaryOperation const& _binaryOperation) Type const& commonType = *_binaryOperation.annotation().commonType; Token::Value const c_op = _binaryOperation.getOperator(); - if (commonType.category() == Type::Category::NumberConstant) + if (commonType.category() == Type::Category::RationalNumber) { - auto const& constantNumber = dynamic_cast(commonType); + auto const& constantNumber = dynamic_cast(commonType); if (constantNumber.denominator() != bigint(1)) error(_binaryOperation, "Fractional numbers not supported."); add("(of_int " + toString(commonType.literalValue(nullptr)) + ")"); @@ -592,9 +592,9 @@ bool Why3Translator::visit(Literal const& _literal) else add("true"); break; - case Type::Category::NumberConstant: + case Type::Category::RationalNumber: { - auto const& constantNumber = dynamic_cast(*type); + auto const& constantNumber = dynamic_cast(*type); if (constantNumber.denominator() != 1) error(_literal, "Fractional numbers not supported."); add("(of_int " + toString(type->literalValue(&_literal)) + ")"); -- cgit v1.2.3 From f67bfd24a3c294f0388f847be213a5236ffd60a4 Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Tue, 29 Mar 2016 15:56:26 -0500 Subject: rational renaming --- libsolidity/analysis/ConstantEvaluator.cpp | 6 +++--- libsolidity/analysis/ReferencesResolver.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index a86e3967..bdd8f61e 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(subType.get())) + if (!dynamic_cast(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(leftType.get())) + if (!dynamic_cast(leftType.get())) BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression.")); - if (!dynamic_cast(rightType.get())) + if (!dynamic_cast(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 3351c716..f4e0f838 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -103,7 +103,7 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) { if (!length->annotation().type) ConstantEvaluator e(*length); - auto const* lengthType = dynamic_cast(length->annotation().type.get()); + auto const* lengthType = dynamic_cast(length->annotation().type.get()); if (!lengthType) fatalTypeError(length->location(), "Invalid array length."); else if (lengthType->denominator() != 1) -- cgit v1.2.3 From f0ea817580d7f8d5c5177adf1a2e39e5e560fefc Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Tue, 29 Mar 2016 16:13:00 -0500 Subject: fixing modulus and Solidity Name and Type Resolution minor fixes current attempts at binary fixup --- libsolidity/ast/Types.cpp | 18 +- libsolidity/ast/Types.h | 2 +- libsolidity/codegen/CompilerUtils.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 272 --------------------- 4 files changed, 10 insertions(+), 284 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4c3947df..8c0d21b1 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -713,8 +713,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ else if (fixedPointType) { value = m_value; - bigint integers = m_value.numerator() / m_value.denominator(); - value -= integers; + rational divisor = m_value / other.m_value; + value -= divisor * m_value; + cout << "MODULO VALUE: " << value << endl; } else value = m_value.numerator() % other.m_value.numerator(); @@ -816,7 +817,7 @@ shared_ptr RationalNumberType::fixedPointType() const bool fractionalSignBit = integers == 0; //sign the fractional side or the integer side bool negative = (m_value < 0); //todo: change name - bigint fractionalBits = fractionalBitsNeeded(); + bigint fractionalBits = findFractionNumberAndBits(); cout << "Total int: " << fractionalBits.str() << endl; if (negative && !fractionalSignBit) // convert to positive number of same bit requirements { @@ -844,23 +845,20 @@ shared_ptr RationalNumberType::fixedPointType() const } //todo: change name of function -bigint RationalNumberType::findFractionNumberAndBits(bool getWholeNumber = false) const +tuple RationalNumberType::findFractionNumberAndBits(bool getWholeNumber) const { rational value; if (getWholeNumber) value = m_value; else value = m_value - wholeNumbers(); - for (unsigned fractionalBits = 0; value < boost::multiprecision::pow(bigint(2), 256); fractionalBits += 8, value *= 256) + for (unsigned fractionalBits = 0; value < boost::multiprecision::pow(bigint(2), 256); fractionalBits += 8, value *= 10) { if (value.denominator() == 1) - return value.numerator()/value.denominator(); - for ( ; value.denominator() != 1 && value < boost::multiprecision::pow(bigint(2), fractionalBits); value *= 10) - if (value.denominator() == 1) - return value.numerator()/value.denominator(); + return make_tuple(value.numerator(), fractionalBits); } cout << "too big :(" << endl; - return value.numerator()/value.denominator(); + return make_tuple(value.numerator()/value.denominator(), fractionalBits); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index ef0a69b3..84236762 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -388,7 +388,7 @@ public: /// @returns the smallest fixed type that can hold the value or an empty pointer std::shared_ptr fixedPointType() const; - bigint fractionalBitsNeeded() const; + std::tuple findFractionNumberAndBits(bool getWholeNumber = false) const; bigint denominator() const { return m_value.denominator(); } bigint wholeNumbers() const { return m_value.numerator() / m_value.denominator(); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 9efb2c29..8589900e 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -345,7 +345,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp break; case Type::Category::Integer: case Type::Category::Contract: - case Type::Category::RationalNumber + case Type::Category::RationalNumber: case Type::Category::FixedPoint: if (targetTypeCategory == Type::Category::FixedBytes) { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 90bb7065..c3878f3e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3441,278 +3441,6 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_rationals) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) -{ - char const* text = R"( - contract test { - function f() { - ufixed0x64 a = 0.12345678; - ufixed8x0 b = 12345678.0; - ufixed0x64 c = 0.00000009; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) -{ - char const* text = R"( - contract test { - function f() { - var a = 0.12345678; - var b = 12345678.0; - var c = 0.00000009; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(invalid_rational_exponent_usage) -{ - char const* text = R"( - contract test { - function f() { - fixed8x8 a = 3 ** 1.5; - fixed24x24 b = 2 ** (1/2); - fixed40x40 c = 42 ** (-1/4); - fixed48x48 d = 16 ** -0.33; - } - } - )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) -{ - char const* text = R"( - contract test { - function f() { - fixed a = 3 ** fixed(1.5); - fixed b = 2 ** fixed(1/2); - fixed c = 42 ** fixed(-1/4); - fixed d = 16 ** fixed(-0.33); - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(rational_unary_operation) -{ - char const* text = R"( - contract test { - function f() { - fixed a = +3.5134; - fixed b = -2.5145; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) -{ - char const* text = R"( - contract test { - function f() { - fixed a = ~3.56; - } - } - )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) -{ - char const* text = R"( - contract test { - function f() { - fixed a = 1.56 | 3; - } - } - )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) -{ - char const* text = R"( - contract test { - function f() { - fixed a = 1.56 ^ 3; - } - } - )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) -{ - char const* text = R"( - contract test { - function f() { - fixed a = 1.56 & 3; - } - } - )"; - - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(valid_fraction_fixed_type) -{ - char const* text = R"( - contract test { - function f(){ - fixed8x8 a = (2**24)/127; - fixed0x8 b = 1/256; - } - } - )"; - - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(valid_fixed_types) -{ - char const* text = R"( - contract test { - function f(){ - fixed8x8 a = 87654321.12345678; - fixed16x16 b = a**2; - fixed24x24 c = b**3; - fixed32x32 d = b**2; - fixed40x40 e = a**5; - } - } - )"; - - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) -{ - char const* text = R"( - contract test { - function f() { - uint128 a = 3; - int128 b = 4; - fixed c = b; - ufixed d = a; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(fixed_type_const_int_conversion) -{ - char const* text = R"( - contract test { - function f() { - fixed c = 3; - ufixed d = 4; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(fixed_type_literal) -{ - char const* text = R"( - contract test { - function f() { - fixed a = 3.14; - ufixed d = 2.555555; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) -{ - char const* text = R"( - contract test { - function f() { - fixed a = 3.14 * 3; - ufixed b = 4 - 2.555555; - fixed c = 1.0 / 3.0; - ufixed d = 599 + .5367; - ufixed e = 35.245 % 12.9; - ufixed f = 1.2 % 2.00000; - fixed g = 2 ** -2; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) -{ - char const* text = R"( - contract test { - function f() { - uint[fixed(3.56)] a; - } - } - )"; - BOOST_CHECK(!success(text)); -} - - -BOOST_AUTO_TEST_CASE(array_declaration_with_rational) -{ - char const* text = R"( - contract test { - function f() { - uint[3.56] a; - } - } - )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) -{ - char const* text = R"( - contract test { - mapping(fixed => string) fixedString; - function f() { - fixedString[3.14] = "Pi"; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(inline_array_fixed_type) -{ - char const* text = R"( - contract test { - function f() { - fixed[3] memory a = [fixed(3.5), fixed(4.1234), fixed(967.32)]; - } - } - )"; - BOOST_CHECK(success(text)); -} - -BOOST_AUTO_TEST_CASE(inline_array_fixed_literals) -{ - char const* text = R"( - contract test { - function f() { - ufixed8x16[3] memory a = [3.5, 4.1234, 2.5]; - } - } - )"; - BOOST_CHECK(success(text)); -} - BOOST_AUTO_TEST_CASE(zero_and_eight_variants_fixed) { char const* text = R"( -- cgit v1.2.3 From 82039b732eb8855a5a9fac228734bf16081071c8 Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Fri, 8 Apr 2016 01:19:20 -0500 Subject: added bytes conversion tests, resolved that, converted to binary scaling, refactored the find algo to prevent large numbers and take into account integer bytes think we're good on solidity type name resolution now removed couts updates to documentation and more removed couts along with literal value implementation forgot semicolons --- libsolidity/ast/Types.cpp | 124 +++++++++++---------- libsolidity/ast/Types.h | 5 +- libsolidity/codegen/LValue.cpp | 1 - test/libsolidity/SolidityEndToEndTest.cpp | 4 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 86 ++++++++++---- 5 files changed, 133 insertions(+), 87 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 8c0d21b1..343a7ea7 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -362,7 +362,6 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPointType::Modifier _modifier): m_integerBits(_integerBits), m_fractionalBits(_fractionalBits), m_modifier(_modifier) { - cout << "FIXED POINT CONSTRUCTOR: " << _integerBits << "x" << _fractionalBits << endl; solAssert( m_integerBits + m_fractionalBits > 0 && m_integerBits + m_fractionalBits <= 256 && @@ -572,14 +571,6 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } else if (_convertTo.category() == Category::FixedPoint) - { - cout << "IMPLICIT CONVERSION" << endl; - if (fixedPointType() && fixedPointType()->isImplicitlyConvertibleTo(_convertTo)) - return true; - - return false; - } - else if (_convertTo.category() == Category::FixedPoint) { if (fixedPointType() && fixedPointType()->isImplicitlyConvertibleTo(_convertTo)) return true; @@ -588,7 +579,10 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const else if (_convertTo.category() == Category::FixedBytes) { FixedBytesType const& fixedBytes = dynamic_cast(_convertTo); - return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + if (m_value.denominator() == 1) + return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + else + return fixedBytes.numBytes() * 8 >= fixedPointType()->numBits(); } return false; } @@ -600,7 +594,6 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const TypePointer intType = integerType(); return intType && intType->isExplicitlyConvertibleTo(_convertTo); } - cout << "EXPLICIT CONVERSION" << endl; TypePointer fixType = fixedPointType(); return fixType && fixType->isExplicitlyConvertibleTo(_convertTo); } @@ -612,7 +605,7 @@ TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) cons { case Token::BitNot: if(m_value.denominator() != 1) - BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + return TypePointer(); value = ~m_value.numerator(); break; case Token::Add: @@ -640,7 +633,6 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } else if (_other->category() == Category::FixedPoint) { - cout << "BINARY OPERATOR RESULTS" << endl; shared_ptr fixType = fixedPointType(); if (!fixType) return TypePointer(); @@ -662,7 +654,6 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } else { - cout << "BINARY OPERATOR RESULTS PART 2" << endl; shared_ptr thisFixedPointType = fixedPointType(); shared_ptr otherFixedPointType = other.fixedPointType(); if (!thisFixedPointType || !otherFixedPointType) @@ -673,23 +664,23 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ else { rational value; - bool fixedPointType = (m_value.denominator() != 1 || other.m_value.denominator() != 1); + bool fractional = (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: - if (fixedPointType) - BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + if (fractional) + return TypePointer(); value = m_value.numerator() | other.m_value.numerator(); break; case Token::BitXor: - if (fixedPointType) - BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + if (fractional) + return TypePointer(); value = m_value.numerator() ^ other.m_value.numerator(); break; case Token::BitAnd: - if (fixedPointType) - BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Cannot perform bit operations on non integer fixed type.")); + if (fractional) + return TypePointer(); value = m_value.numerator() & other.m_value.numerator(); break; case Token::Add: @@ -700,7 +691,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ break; case Token::Mul: value = m_value * other.m_value; - break; + break; case Token::Div: if (other.m_value == 0) return TypePointer(); @@ -710,19 +701,22 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ case Token::Mod: if (other.m_value == 0) return TypePointer(); - else if (fixedPointType) + else if (fractional) { value = m_value; - rational divisor = m_value / other.m_value; - value -= divisor * m_value; - cout << "MODULO VALUE: " << value << endl; + if (value > other.m_value) + { + do + { + value -= other.m_value; + } while (value > other.m_value); + } } else value = m_value.numerator() % other.m_value.numerator(); break; case Token::Exp: { - cout << "Is this the source of the problem" << endl; bigint newDenominator; bigint newNumerator; if (other.m_value.denominator() != 1) @@ -769,14 +763,17 @@ string RationalNumberType::toString(bool) const u256 RationalNumberType::literalValue(Literal const*) const { u256 value; + unsigned uselessBits = 0; + bigint shiftedValue; + tie(shiftedValue, uselessBits) = findFractionNumberAndBits(); // we ignore the literal and hope that the type was correctly determined - - solAssert(m_value >= -(bigint(1) << 255), "Number constant too small."); + solAssert(shiftedValue <= u256(-1), "Integer constant too large."); + solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); if (m_value >= 0) - value = u256(m_value.numerator()); + value = u256(shiftedValue); else - value = s2u(s256(m_value.numerator())); + value = s2u(s256(0 - shiftedValue)); return value; } @@ -794,6 +791,7 @@ TypePointer RationalNumberType::mobileType() const return fixType; } +//TODO: combine integerType() and fixedPointType() into one function shared_ptr RationalNumberType::integerType() const { bigint value = wholeNumbers(); @@ -813,52 +811,62 @@ shared_ptr RationalNumberType::fixedPointType() const { //do calculations up here bigint integers = wholeNumbers(); - //bigint _remainder = abs(m_value.numerator() % m_value.denominator()); + bigint shiftedValue; + unsigned integerBits = 0; + unsigned fractionalBits = 0; bool fractionalSignBit = integers == 0; //sign the fractional side or the integer side bool negative = (m_value < 0); - //todo: change name - bigint fractionalBits = findFractionNumberAndBits(); - cout << "Total int: " << fractionalBits.str() << endl; + if (negative && !fractionalSignBit) // convert to positive number of same bit requirements { integers = ((0 - integers) - 1) << 1; - fractionalBits = ((0 - fractionalBits) - 1) << 1; + integerBits = max(bytesRequired(integers), 1u) * 8; + tie(shiftedValue, fractionalBits) = findFractionNumberAndBits(integerBits); } else if (negative && fractionalSignBit) - fractionalBits = ((0 - fractionalBits) - 1) << 1; - - if (fractionalBits > u256(-1)) - return shared_ptr(); + tie(shiftedValue, fractionalBits) = findFractionNumberAndBits(); else { - cout << "m_value: " << m_value << endl; - cout << "Total int: " << fractionalBits.str() << endl; - unsigned fractionalBytesRequired = bytesRequired(fractionalBits) * 8; - unsigned integerBytesRequired = bytesRequired(integers) * 8; - cout << "Fractional Bytes Required: " << fractionalBytesRequired << endl; - cout << "Integer Bytes Required: " << integerBytesRequired << endl << endl; + if (!fractionalSignBit) + integerBits = max(bytesRequired(integers), 1u) * 8; + tie(shiftedValue, fractionalBits) = findFractionNumberAndBits(integerBits); + if (shiftedValue == 0 && fractionalSignBit) + { + integerBits = 8; + fractionalBits = 8; + } + } + + if (shiftedValue > u256(-1) || integers > u256(-1)) + return shared_ptr(); + else return make_shared( - integerBytesRequired, fractionalBytesRequired, + integerBits, fractionalBits, negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned ); - } } //todo: change name of function -tuple RationalNumberType::findFractionNumberAndBits(bool getWholeNumber) const +tuple RationalNumberType::findFractionNumberAndBits(unsigned const restrictedBits) const { - rational value; - if (getWholeNumber) - value = m_value; - else - value = m_value - wholeNumbers(); - for (unsigned fractionalBits = 0; value < boost::multiprecision::pow(bigint(2), 256); fractionalBits += 8, value *= 10) + bool isNegative = m_value < 0; + rational value = abs(m_value); + unsigned fractionalBits = 0; + for (; fractionalBits <= 256 - restrictedBits; fractionalBits += 8, value *= 256) { if (value.denominator() == 1) return make_tuple(value.numerator(), fractionalBits); - } - cout << "too big :(" << endl; - return make_tuple(value.numerator()/value.denominator(), fractionalBits); + bigint predictionValue = 256 * (value.numerator() / value.denominator()); + if (predictionValue > u256(-1)) + return make_tuple(value.numerator()/value.denominator(), fractionalBits); + predictionValue = ((0 - predictionValue) - 1) << 1; + if (predictionValue > u256(-1) && isNegative) + // essentially asking if its negative and if so will giving it a sign bit value put it over the limit + // if we also multiply it one more time by 256 + return make_tuple(((0 - value.numerator() / value.denominator()) - 1) << 1, fractionalBits); + + } + return make_tuple(value.numerator()/value.denominator(), 256 - restrictedBits); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 84236762..03b6563c 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -385,10 +385,11 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr integerType() const; - /// @returns the smallest fixed type that can hold the value or an empty pointer + /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. + /// If the integer part does not fit, returns an empty pointer. std::shared_ptr fixedPointType() const; - std::tuple findFractionNumberAndBits(bool getWholeNumber = false) const; + std::tuple findFractionNumberAndBits(unsigned const restrictedBits = 0) const; bigint denominator() const { return m_value.denominator(); } bigint wholeNumbers() const { return m_value.numerator() / m_value.denominator(); } diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 7d9fa4c8..1d1956aa 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -243,7 +243,6 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc 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/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c3bac3d7..67748c1f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6634,7 +6634,7 @@ BOOST_AUTO_TEST_CASE(delete_on_array_of_structs) } -BOOST_AUTO_TEST_CASE(fixed_data_type) +/*BOOST_AUTO_TEST_CASE(fixed_data_type) { char const* sourceCode = R"( contract C { @@ -6654,7 +6654,7 @@ BOOST_AUTO_TEST_CASE(fixed_data_type_expression) } )"; compileAndRun(sourceCode, 0, "C"); -} +}*/ BOOST_AUTO_TEST_CASE(internal_library_function) { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c3878f3e..a4eec7e6 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3278,16 +3278,16 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(valid_fixed_types) +BOOST_AUTO_TEST_CASE(valid_fixed_types_casting) { char const* text = R"( contract test { function f(){ - fixed8x8 a = 87654321.12345678; - fixed16x16 b = a**2; - fixed24x24 c = b**3; - fixed32x32 d = b**2; - fixed40x40 e = a**5; + ufixed8x8 a = ufixed8x8(8765.1234); + ufixed16x16 b = a**2; + ufixed24x24 c = b**3; + ufixed32x32 d = b**2; + ufixed40x40 e = a**5; } } )"; @@ -3310,7 +3310,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_const_int_conversion) +BOOST_AUTO_TEST_CASE(fixed_type_rational_conversion) { char const* text = R"( contract test { @@ -3328,8 +3328,8 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal) char const* text = R"( contract test { function f() { - fixed a = 3.14; - ufixed d = 2.555555; + fixed a = 4.5; + ufixed d = 2.5; } } )"; @@ -3341,12 +3341,12 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) char const* text = R"( contract test { function f() { - fixed a = 3.14 * 3; - ufixed b = 4 - 2.555555; - fixed c = 1.0 / 3.0; - ufixed d = 599 + .5367; - ufixed e = 35.245 % 12.9; - ufixed f = 1.2 % 2.00000; + ufixed8x248 a = 3.14 * 3; + ufixed8x248 b = 4 - 2.555555; + ufixed0x256 c = 1.0 / 3.0; + ufixed16x240 d = 599 + .5367; + ufixed8x248 e = 35.245 % 12.9; + ufixed8x248 f = 1.2 % 2; fixed g = 2 ** -2; } } @@ -3354,6 +3354,19 @@ BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(rational_as_exponent_value) +{ + char const* text = R"( + contract test { + function f() { + fixed g = 2 ** -2.2; + fixed b = 3 ** 2.56; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_CASE(fixed_type_invalid_size_conversion) { char const* text = R"( @@ -3408,7 +3421,7 @@ BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) { char const* text = R"( contract test { - mapping(fixed => string) fixedString; + mapping(ufixed8x248 => string) fixedString; function f() { fixedString[3.14] = "Pi"; } @@ -3434,7 +3447,7 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_rationals) char const* text = R"( contract test { function f() { - ufixed8x16[4] memory a = [3.5, 4.1234, 2.5, 4.0]; + ufixed8x248[4] memory a = [3.5, 4.1234, 2.5, 4.0]; } } )"; @@ -3445,8 +3458,8 @@ BOOST_AUTO_TEST_CASE(zero_and_eight_variants_fixed) { char const* text = R"( contract A { - fixed8x0 someInt = 4; - fixed0x8 half = 0.5; + ufixed8x0 someInt = 4; + ufixed0x8 half = 0.5; } )"; BOOST_CHECK(success(text)); @@ -3457,9 +3470,9 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) char const* text = R"( contract test { function f() { - ufixed0x8 a = 0.12345678; - ufixed8x0 b = 12345678.0; - ufixed0x8 c = 0.00000009; + ufixed0x256 a = 0.12345678; + ufixed24x0 b = 12345678.0; + ufixed0x256 c = 0.00000009; } } )"; @@ -3510,13 +3523,38 @@ BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) +{ + char const* text = R"( + contract test { + function f() { + bytes32 c = 3.183; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) +{ + char const* text = R"( + contract test { + function f() { + fixed a = 3.183; + bytes32 c = a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_CASE(rational_unary_operation) { char const* text = R"( contract test { function f() { - fixed a = +3.5134; - fixed b = -2.5145; + ufixed8x248 a = +3.5134; + fixed8x248 b = -3.5134; } } )"; -- cgit v1.2.3 From 4b3e1f140c80a50d0682f877215fbaeab3847aab Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Mon, 11 Apr 2016 16:35:17 -0500 Subject: much better way of doing modulus --- libsolidity/ast/Types.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 343a7ea7..7558d715 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -703,14 +703,8 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ return TypePointer(); else if (fractional) { - value = m_value; - if (value > other.m_value) - { - do - { - value -= other.m_value; - } while (value > other.m_value); - } + rational tempValue = m_value / other.m_value; + value = m_value - (tempValue.numerator() / tempValue.denominator()) * other.m_value; } else value = m_value.numerator() % other.m_value.numerator(); -- cgit v1.2.3 From 5bddb2d6ffdd8a2c02a61cc304f2743f55dcb5f9 Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Mon, 11 Apr 2016 18:12:11 -0500 Subject: changed algorithm for finding bits --- libsolidity/ast/Types.cpp | 66 ++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 7558d715..366fb6af 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -572,6 +572,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const } else if (_convertTo.category() == Category::FixedPoint) { + cout << "hit here?" << endl; if (fixedPointType() && fixedPointType()->isImplicitlyConvertibleTo(_convertTo)) return true; return false; @@ -803,41 +804,48 @@ shared_ptr RationalNumberType::integerType() const shared_ptr RationalNumberType::fixedPointType() const { - //do calculations up here - bigint integers = wholeNumbers(); - bigint shiftedValue; - unsigned integerBits = 0; - unsigned fractionalBits = 0; - bool fractionalSignBit = integers == 0; //sign the fractional side or the integer side bool negative = (m_value < 0); - - if (negative && !fractionalSignBit) // convert to positive number of same bit requirements - { - integers = ((0 - integers) - 1) << 1; - integerBits = max(bytesRequired(integers), 1u) * 8; - tie(shiftedValue, fractionalBits) = findFractionNumberAndBits(integerBits); - } - else if (negative && fractionalSignBit) - tie(shiftedValue, fractionalBits) = findFractionNumberAndBits(); + unsigned fractionalBits = 0; + unsigned integerBits = bytesRequired(wholeNumbers()) * 8; + rational value = m_value; + bigint l = bigint(1) << 256; + rational maxValue = rational(l); + if (!negative) + maxValue -= 1; else + value = -value; + cout << "Max value: " << maxValue << endl; + while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256 - integerBits) { - if (!fractionalSignBit) - integerBits = max(bytesRequired(integers), 1u) * 8; - tie(shiftedValue, fractionalBits) = findFractionNumberAndBits(integerBits); - if (shiftedValue == 0 && fractionalSignBit) - { - integerBits = 8; - fractionalBits = 8; - } + value *= 0x100; + fractionalBits += 8; } - if (shiftedValue > u256(-1) || integers > u256(-1)) + if (value > maxValue) + { + cout << "value > max value " << endl; return shared_ptr(); - else - return make_shared( - integerBits, fractionalBits, - negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned - ); + } + bigint v = value.denominator() / value.numerator(); + if (negative) + v = -v; + // u256(v) is the actual value that will be put on the stack + // From here on, very similar to integerType() + //if (negative) // convert to positive number of same bit requirements + // value = ((0 - value) - 1) << 1; + if (value > u256(-1)) + { + cout << "Too large of a number" << endl; + return shared_ptr(); + } + + cout << "integer bits: " << integerBits << ", fractionalBits: " << fractionalBits << endl; + //solAssert(integerBits >= fractionalBits, "Invalid bit requirement calculation."); + //@todo special handling for integerBits == 0 && fractionalBits == 0? + return make_shared( + integerBits, fractionalBits, + negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned + ); } //todo: change name of function -- cgit v1.2.3 From bfc238c8d11e118443d373d819deeada9fe1ea3b Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Tue, 12 Apr 2016 12:36:34 -0500 Subject: updated algorithm for bit finding...now to figure out literal value tiny fixups changed location of the check got rid of extra space and fixed a couple of things added binary results bits change back literal value --- libsolidity/analysis/ReferencesResolver.cpp | 6 +- libsolidity/analysis/TypeChecker.cpp | 21 ++- libsolidity/ast/Types.cpp | 203 +++++++++------------ libsolidity/ast/Types.h | 8 +- libsolidity/parsing/Token.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 7 +- 6 files changed, 103 insertions(+), 144 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index f4e0f838..9f83971b 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -104,10 +104,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) if (!length->annotation().type) ConstantEvaluator e(*length); auto const* lengthType = dynamic_cast(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."); + if (!lengthType || lengthType->denominator() != 1) + fatalTypeError(length->location(), "Invalid array length, expected integer literal."); else _typeName.annotation().type = make_shared(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index fb0c665c..1b37d42e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -774,9 +774,10 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) solAssert(!var.typeName(), ""); if ( valueComponentType->category() == Type::Category::RationalNumber && - !dynamic_pointer_cast(valueComponentType)->integerType() + !dynamic_pointer_cast(valueComponentType)->integerType() && + !dynamic_pointer_cast(valueComponentType)->fixedPointType() ) - fatalTypeError(_statement.initialValue()->location(), "Invalid integer constant " + valueComponentType->toString() + "."); + fatalTypeError(_statement.initialValue()->location(), "Invalid rational " + valueComponentType->toString() + "."); var.annotation().type = valueComponentType->mobileType(); var.accept(*this); } @@ -800,8 +801,11 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) void TypeChecker::endVisit(ExpressionStatement const& _statement) { if (type(_statement.expression())->category() == Type::Category::RationalNumber) - if (!dynamic_pointer_cast(type(_statement.expression()))->integerType()) - typeError(_statement.expression().location(), "Invalid integer constant."); + if ( + !dynamic_pointer_cast(type(_statement.expression()))->integerType() && + !dynamic_pointer_cast(type(_statement.expression()))->fixedPointType() + ) + typeError(_statement.expression().location(), "Invalid rational number."); } bool TypeChecker::visit(Conditional const& _conditional) @@ -1107,8 +1111,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (functionType->takesArbitraryParameters()) { if (auto t = dynamic_cast(argType.get())) - if (!t->integerType()) - typeError(arguments[i]->location(), "Integer constant too large."); + if (!t->integerType() && !t->fixedPointType()) + typeError(arguments[i]->location(), "Rational number too large."); } else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) typeError( @@ -1343,8 +1347,7 @@ bool TypeChecker::visit(IndexAccess const& _access) expectType(*index, IntegerType(256)); if (auto numberType = dynamic_cast(type(*index).get())) { - if (numberType->denominator() != 1) - typeError(_access.location(), "Invalid type for array access."); + solAssert(!numberType->denominator() != 1 ,"Invalid type for array access."); if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) typeError(_access.location(), "Out of bounds array access."); } @@ -1371,7 +1374,7 @@ bool TypeChecker::visit(IndexAccess const& _access) resultType = make_shared(make_shared(DataLocation::Memory, typeType.actualType())); else { - index->accept(*this); + expectType(*index, IntegerType(256)); if (auto length = dynamic_cast(type(*index).get())) resultType = make_shared(make_shared( DataLocation::Memory, diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 366fb6af..865e5969 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -181,10 +181,13 @@ TypePointer Type::forLiteral(Literal const& _literal) case Token::FalseLiteral: return make_shared(); case Token::Number: - if (RationalNumberType::isValidLiteral(_literal)) - return make_shared(_literal); + { + tuple validLiteral = RationalNumberType::isValidLiteral(_literal); + if (get<0>(validLiteral) == true) + return make_shared(get<1>(validLiteral)); else return TypePointer(); + } case Token::StringLiteral: return make_shared(_literal); default: @@ -272,7 +275,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const else if (_convertTo.category() == Category::FixedPoint) { FixedPointType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.integerBits() < m_bits) + if (convertTo.integerBits() < m_bits || isAddress()) return false; else if (isSigned()) return convertTo.isSigned(); @@ -440,7 +443,8 @@ string FixedPointType::toString(bool) const TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->category() != Category::RationalNumber + if ( + _other->category() != Category::RationalNumber && _other->category() != category() && _other->category() != Category::Integer ) @@ -453,22 +457,29 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi // All fixed types can be compared if (Token::isCompareOp(_operator)) return commonType; - if (Token::isBooleanOp(_operator)) + if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator)) return TypePointer(); return commonType; } -bool RationalNumberType::isValidLiteral(Literal const& _literal) +tuple RationalNumberType::isValidLiteral(Literal const& _literal) { + rational x; try { 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. + if ( + !all_of(radixPoint + 1, _literal.value().end(), ::isdigit) || + !all_of(_literal.value().begin(), radixPoint, ::isdigit) + ) + throw; + //Only decimal notation allowed here, leading zeros would switch to octal. auto leadingZeroes = find_if_not( radixPoint + 1, _literal.value().end(), @@ -476,81 +487,55 @@ bool RationalNumberType::isValidLiteral(Literal const& _literal) ); 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; + x = numerator + denominator; } else - rational x = bigint(_literal.value()); + x = bigint(_literal.value()); + switch (_literal.subDenomination()) + { + case Literal::SubDenomination::None: + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::Second: + break; + case Literal::SubDenomination::Szabo: + x *= bigint("1000000000000"); + break; + case Literal::SubDenomination::Finney: + x *= bigint("1000000000000000"); + break; + case Literal::SubDenomination::Ether: + x *= bigint("1000000000000000000"); + break; + case Literal::SubDenomination::Minute: + x *= bigint("60"); + break; + case Literal::SubDenomination::Hour: + x *= bigint("3600"); + break; + case Literal::SubDenomination::Day: + x *= bigint("86400"); + break; + case Literal::SubDenomination::Week: + x *= bigint("604800"); + break; + case Literal::SubDenomination::Year: + x *= bigint("31536000"); + break; + } + } catch (...) { - return false; - } - return true; -} - -RationalNumberType::RationalNumberType(Literal const& _literal) -{ - 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: - break; - case Literal::SubDenomination::Szabo: - m_value *= bigint("1000000000000"); - break; - case Literal::SubDenomination::Finney: - m_value *= bigint("1000000000000000"); - break; - case Literal::SubDenomination::Ether: - m_value *= bigint("1000000000000000000"); - break; - case Literal::SubDenomination::Minute: - m_value *= bigint("60"); - break; - case Literal::SubDenomination::Hour: - m_value *= bigint("3600"); - break; - case Literal::SubDenomination::Day: - m_value *= bigint("86400"); - break; - case Literal::SubDenomination::Week: - m_value *= bigint("604800"); - break; - case Literal::SubDenomination::Year: - m_value *= bigint("31536000"); - break; + return make_tuple(false, rational(0)); } + return make_tuple(true, x); } bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -560,19 +545,20 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const auto targetType = dynamic_cast(&_convertTo); if (m_value == 0) return true; + if (m_value.denominator() != 1) + return false; int forSignBit = (targetType->isSigned() ? 1 : 0); if (m_value > 0) { - if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + if (integerPart() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) return true; } - else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit))) + else if (targetType->isSigned() && -integerPart() <= (u256(1) << (targetType->numBits() - forSignBit))) return true; return false; } else if (_convertTo.category() == Category::FixedPoint) { - cout << "hit here?" << endl; if (fixedPointType() && fixedPointType()->isImplicitlyConvertibleTo(_convertTo)) return true; return false; @@ -583,7 +569,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const if (m_value.denominator() == 1) return fixedBytes.numBytes() * 8 >= integerType()->numBits(); else - return fixedBytes.numBytes() * 8 >= fixedPointType()->numBits(); + return false; } return false; } @@ -692,7 +678,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ break; case Token::Mul: value = m_value * other.m_value; - break; + break; case Token::Div: if (other.m_value == 0) return TypePointer(); @@ -758,9 +744,7 @@ string RationalNumberType::toString(bool) const u256 RationalNumberType::literalValue(Literal const*) const { u256 value; - unsigned uselessBits = 0; - bigint shiftedValue; - tie(shiftedValue, uselessBits) = findFractionNumberAndBits(); + bigint shiftedValue = integerPart(); // we ignore the literal and hope that the type was correctly determined solAssert(shiftedValue <= u256(-1), "Integer constant too large."); solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); @@ -768,8 +752,7 @@ u256 RationalNumberType::literalValue(Literal const*) const if (m_value >= 0) value = u256(shiftedValue); else - value = s2u(s256(0 - shiftedValue)); - + value = s2u(s256(shiftedValue)); return value; } @@ -789,7 +772,7 @@ TypePointer RationalNumberType::mobileType() const //TODO: combine integerType() and fixedPointType() into one function shared_ptr RationalNumberType::integerType() const { - bigint value = wholeNumbers(); + bigint value = integerPart(); bool negative = (value < 0); if (negative) // convert to positive number of same bit requirements value = ((0 - value) - 1) << 1; @@ -806,26 +789,34 @@ shared_ptr RationalNumberType::fixedPointType() const { bool negative = (m_value < 0); unsigned fractionalBits = 0; - unsigned integerBits = bytesRequired(wholeNumbers()) * 8; + unsigned integerBits = 0; rational value = m_value; - bigint l = bigint(1) << 256; - rational maxValue = rational(l); + bigint transitionValue = bigint(1) << 256; + rational maxValue = rational(transitionValue); + if (!negative) + { maxValue -= 1; + integerBits = bytesRequired(integerPart()) * 8; + } else - value = -value; - cout << "Max value: " << maxValue << endl; - while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256 - integerBits) { + value = abs(value); + if (integerPart() > 0) + transitionValue = ((0 - integerPart()) - 1) << 1; + else + transitionValue = 0; + integerBits = bytesRequired(transitionValue) * 8; + } + + while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256 - integerBits) + { value *= 0x100; fractionalBits += 8; } if (value > maxValue) - { - cout << "value > max value " << endl; return shared_ptr(); - } bigint v = value.denominator() / value.numerator(); if (negative) v = -v; @@ -834,12 +825,7 @@ shared_ptr RationalNumberType::fixedPointType() const //if (negative) // convert to positive number of same bit requirements // value = ((0 - value) - 1) << 1; if (value > u256(-1)) - { - cout << "Too large of a number" << endl; return shared_ptr(); - } - - cout << "integer bits: " << integerBits << ", fractionalBits: " << fractionalBits << endl; //solAssert(integerBits >= fractionalBits, "Invalid bit requirement calculation."); //@todo special handling for integerBits == 0 && fractionalBits == 0? return make_shared( @@ -848,31 +834,6 @@ shared_ptr RationalNumberType::fixedPointType() const ); } -//todo: change name of function -tuple RationalNumberType::findFractionNumberAndBits(unsigned const restrictedBits) const -{ - bool isNegative = m_value < 0; - rational value = abs(m_value); - unsigned fractionalBits = 0; - for (; fractionalBits <= 256 - restrictedBits; fractionalBits += 8, value *= 256) - { - if (value.denominator() == 1) - return make_tuple(value.numerator(), fractionalBits); - bigint predictionValue = 256 * (value.numerator() / value.denominator()); - if (predictionValue > u256(-1)) - return make_tuple(value.numerator()/value.denominator(), fractionalBits); - predictionValue = ((0 - predictionValue) - 1) << 1; - if (predictionValue > u256(-1) && isNegative) - // essentially asking if its negative and if so will giving it a sign bit value put it over the limit - // if we also multiply it one more time by 256 - return make_tuple(((0 - value.numerator() / value.denominator()) - 1) << 1, fractionalBits); - - } - return make_tuple(value.numerator()/value.denominator(), 256 - restrictedBits); -} - - - StringLiteralType::StringLiteralType(Literal const& _literal): m_value(_literal.value()) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 03b6563c..5e826684 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -363,9 +363,8 @@ public: virtual Category category() const override { return Category::RationalNumber; } /// @returns true if the literal is a valid integer. - static bool isValidLiteral(Literal const& _literal); - - explicit RationalNumberType(Literal const& _literal); + static std::tuple isValidLiteral(Literal const& _literal); + explicit RationalNumberType(rational _value): m_value(_value) {} @@ -389,9 +388,8 @@ public: /// If the integer part does not fit, returns an empty pointer. std::shared_ptr fixedPointType() const; - std::tuple findFractionNumberAndBits(unsigned const restrictedBits = 0) const; bigint denominator() const { return m_value.denominator(); } - bigint wholeNumbers() const { return m_value.numerator() / m_value.denominator(); } + bigint integerPart() const { return m_value.numerator() / m_value.denominator(); } private: rational m_value; diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index ef817d5d..0ab97988 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -158,7 +158,7 @@ tuple Token::fromIdentifierOrKeyword(s int n = parseSize(positionX + 1, _literal.end()); if ( 0 <= m && m <= 256 && - 0 <= n && n <= 256 && + 8 <= n && n <= 256 && m + n > 0 && m + n <= 256 && m % 8 == 0 && diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a4eec7e6..aea2c688 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3454,11 +3454,10 @@ BOOST_AUTO_TEST_CASE(inline_array_fixed_rationals) BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(zero_and_eight_variants_fixed) +BOOST_AUTO_TEST_CASE(zero_and_eight_variant_fixed) { char const* text = R"( contract A { - ufixed8x0 someInt = 4; ufixed0x8 half = 0.5; } )"; @@ -3471,7 +3470,7 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) contract test { function f() { ufixed0x256 a = 0.12345678; - ufixed24x0 b = 12345678.0; + ufixed24x8 b = 12345678.5; ufixed0x256 c = 0.00000009; } } @@ -3532,7 +3531,7 @@ BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) } } )"; - BOOST_CHECK(success(text)); + BOOST_CHECK(!success(text)); } BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) -- cgit v1.2.3 From a6fc3c8f30d987f90d791c6cc5ec9d0a6a132109 Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Thu, 5 May 2016 17:47:08 -0500 Subject: reorganized tests and fixed mobile types and implicit conversions of rationals and fixed point types one final tweak check for null types --- libsolidity/analysis/TypeChecker.cpp | 72 +++++-- libsolidity/ast/Types.cpp | 165 ++++++++-------- test/libsolidity/SolidityNameAndTypeResolution.cpp | 207 +++++++++++++-------- 3 files changed, 263 insertions(+), 181 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1b37d42e..264fee6b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -774,8 +774,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) solAssert(!var.typeName(), ""); if ( valueComponentType->category() == Type::Category::RationalNumber && - !dynamic_pointer_cast(valueComponentType)->integerType() && - !dynamic_pointer_cast(valueComponentType)->fixedPointType() + !dynamic_pointer_cast(valueComponentType)->mobileType() ) fatalTypeError(_statement.initialValue()->location(), "Invalid rational " + valueComponentType->toString() + "."); var.annotation().type = valueComponentType->mobileType(); @@ -785,14 +784,32 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { var.accept(*this); if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type)) - typeError( - _statement.location(), - "Type " + - valueComponentType->toString() + - " is not implicitly convertible to expected type " + - var.annotation().type->toString() + - "." - ); + { + if ( + valueComponentType->category() == Type::Category::RationalNumber && + dynamic_pointer_cast(valueComponentType)->denominator() != 1 && + !!valueComponentType->mobileType() + ) + typeError( + _statement.location(), + "Type " + + valueComponentType->toString() + + " is not implicitly convertible to expected type " + + var.annotation().type->toString() + + ". Try converting to type " + + valueComponentType->mobileType()->toString() + + " or use an explicit conversion." + ); + else + typeError( + _statement.location(), + "Type " + + valueComponentType->toString() + + " is not implicitly convertible to expected type " + + var.annotation().type->toString() + + "." + ); + } } } return false; @@ -1499,16 +1516,33 @@ Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) { _expression.accept(*this); - if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) - typeError( - _expression.location(), - "Type " + - type(_expression)->toString() + - " is not implicitly convertible to expected type " + - _expectedType.toString() + - "." - ); + { + if ( + type(_expression)->category() == Type::Category::RationalNumber && + dynamic_pointer_cast(type(_expression))->denominator() != 1 && + !!type(_expression)->mobileType() + ) + typeError( + _expression.location(), + "Type " + + type(_expression)->toString() + + " is not implicitly convertible to expected type " + + _expectedType.toString() + + ". Try converting to type " + + type(_expression)->mobileType()->toString() + + " or using an explicit conversion." + ); + else + typeError( + _expression.location(), + "Type " + + type(_expression)->toString() + + " is not implicitly convertible to expected type " + + _expectedType.toString() + + "." + ); + } } void TypeChecker::requireLValue(Expression const& _expression) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 865e5969..1dbb70e2 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -387,18 +387,8 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const else return !convertTo.isSigned() || (convertTo.m_integerBits > m_integerBits); } - else if (_convertTo.category() == Category::Integer) - { - IntegerType const& convertTo = dynamic_cast(_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; + + return false; } bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -459,6 +449,8 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi return commonType; if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator)) return TypePointer(); + if (Token::Exp == _operator) + return TypePointer(); return commonType; } @@ -470,13 +462,12 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal rational numerator; rational denominator(1); - auto radixPoint = find(_literal.value().begin(), _literal.value().end(), '.'); - + auto radixPoint = find(_literal.value().begin(), _literal.value().end(), '.'); if (radixPoint != _literal.value().end()) { if ( !all_of(radixPoint + 1, _literal.value().end(), ::isdigit) || - !all_of(_literal.value().begin(), radixPoint, ::isdigit) + !all_of(_literal.value().begin(), radixPoint, ::isdigit) ) throw; //Only decimal notation allowed here, leading zeros would switch to octal. @@ -485,8 +476,7 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal _literal.value().end(), [](char const& a) { return a == '0'; } ); - auto fractionalBegin = leadingZeroes != _literal.value().end() ? - leadingZeroes : radixPoint + 1; + auto fractionalBegin = leadingZeroes; denominator = bigint(string(fractionalBegin, _literal.value().end())); denominator /= boost::multiprecision::pow( @@ -498,43 +488,44 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal } else x = bigint(_literal.value()); - switch (_literal.subDenomination()) - { - case Literal::SubDenomination::None: - case Literal::SubDenomination::Wei: - case Literal::SubDenomination::Second: - break; - case Literal::SubDenomination::Szabo: - x *= bigint("1000000000000"); - break; - case Literal::SubDenomination::Finney: - x *= bigint("1000000000000000"); - break; - case Literal::SubDenomination::Ether: - x *= bigint("1000000000000000000"); - break; - case Literal::SubDenomination::Minute: - x *= bigint("60"); - break; - case Literal::SubDenomination::Hour: - x *= bigint("3600"); - break; - case Literal::SubDenomination::Day: - x *= bigint("86400"); - break; - case Literal::SubDenomination::Week: - x *= bigint("604800"); - break; - case Literal::SubDenomination::Year: - x *= bigint("31536000"); - break; - } - } catch (...) { return make_tuple(false, rational(0)); } + switch (_literal.subDenomination()) + { + case Literal::SubDenomination::None: + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::Second: + break; + case Literal::SubDenomination::Szabo: + x *= bigint("1000000000000"); + break; + case Literal::SubDenomination::Finney: + x *= bigint("1000000000000000"); + break; + case Literal::SubDenomination::Ether: + x *= bigint("1000000000000000000"); + break; + case Literal::SubDenomination::Minute: + x *= bigint("60"); + break; + case Literal::SubDenomination::Hour: + x *= bigint("3600"); + break; + case Literal::SubDenomination::Day: + x *= bigint("86400"); + break; + case Literal::SubDenomination::Week: + x *= bigint("604800"); + break; + case Literal::SubDenomination::Year: + x *= bigint("31536000"); + break; + } + + return make_tuple(true, x); } @@ -559,8 +550,14 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const } else if (_convertTo.category() == Category::FixedPoint) { - if (fixedPointType() && fixedPointType()->isImplicitlyConvertibleTo(_convertTo)) - return true; + //call fixed point type...call fractional bits...shift our number by the number of fractional bits... + //...see if it's a whole number. Make helper function for whether or not finitely representable. + if (auto fixed = fixedPointType()) + { + rational value = m_value * boost::multiprecision::pow(bigint(2), fixed->fractionalBits()); + if (value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo)) + return true; + } return false; } else if (_convertTo.category() == Category::FixedBytes) @@ -744,7 +741,17 @@ string RationalNumberType::toString(bool) const u256 RationalNumberType::literalValue(Literal const*) const { u256 value; - bigint shiftedValue = integerPart(); + bigint shiftedValue; + + if (m_value.denominator() != 1) + { + rational temporaryValue = m_value; + auto fixed = fixedPointType(); + temporaryValue *= boost::multiprecision::pow(bigint(2), fixed->fractionalBits()); + shiftedValue = temporaryValue.numerator() / temporaryValue.denominator(); + } + else + shiftedValue = integerPart(); // we ignore the literal and hope that the type was correctly determined solAssert(shiftedValue <= u256(-1), "Integer constant too large."); solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); @@ -772,6 +779,7 @@ TypePointer RationalNumberType::mobileType() const //TODO: combine integerType() and fixedPointType() into one function shared_ptr RationalNumberType::integerType() const { + solAssert(m_value.denominator() == 1, "Non integer type found."); bigint value = integerPart(); bool negative = (value < 0); if (negative) // convert to positive number of same bit requirements @@ -788,46 +796,43 @@ shared_ptr RationalNumberType::integerType() const shared_ptr RationalNumberType::fixedPointType() const { bool negative = (m_value < 0); + bigint fillRationalBits = bigint(1) << 256; //use this because rationals don't have bit ops unsigned fractionalBits = 0; unsigned integerBits = 0; - rational value = m_value; - bigint transitionValue = bigint(1) << 256; - rational maxValue = rational(transitionValue); + rational value = abs(m_value); //convert to absolute value of same type for byte requirements + rational maxValue = negative ? + rational(fillRationalBits) / 2: + rational(fillRationalBits) - 1; - if (!negative) - { - maxValue -= 1; - integerBits = bytesRequired(integerPart()) * 8; - } - else + while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256) { - value = abs(value); - if (integerPart() > 0) - transitionValue = ((0 - integerPart()) - 1) << 1; - else - transitionValue = 0; - integerBits = bytesRequired(transitionValue) * 8; - } - - while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256 - integerBits) - { value *= 0x100; fractionalBits += 8; } - + if (value > maxValue) return shared_ptr(); - bigint v = value.denominator() / value.numerator(); - if (negative) - v = -v; // u256(v) is the actual value that will be put on the stack // From here on, very similar to integerType() - //if (negative) // convert to positive number of same bit requirements - // value = ((0 - value) - 1) << 1; - if (value > u256(-1)) + bigint v = value.numerator() / value.denominator(); + if (negative) //convert back to negative number and then shift into a positive number of equal size + v = (v - 1) << 1; + + if (v > u256(-1)) + return shared_ptr(); + if (0 == integerPart()) + integerBits = 0; + else + integerBits = (bytesRequired(v) * 8) - fractionalBits; + + if (integerBits > 256 || fractionalBits > 256 || fractionalBits + integerBits > 256) return shared_ptr(); - //solAssert(integerBits >= fractionalBits, "Invalid bit requirement calculation."); - //@todo special handling for integerBits == 0 && fractionalBits == 0? + if (integerBits + fractionalBits == 0) + { + integerBits = 0; + fractionalBits = 8; + } + return make_shared( integerBits, fractionalBits, negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index aea2c688..48b58b71 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3278,286 +3278,316 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(valid_fixed_types_casting) +BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) { char const* text = R"( contract test { - function f(){ - ufixed8x8 a = ufixed8x8(8765.1234); - ufixed16x16 b = a**2; - ufixed24x24 c = b**3; - ufixed32x32 d = b**2; - ufixed40x40 e = a**5; + function f() { + uint128 a = 3; + int128 b = 4; + fixed c = b; + ufixed d = a; } } )"; - BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) +BOOST_AUTO_TEST_CASE(fixed_type_rational_int_conversion) { char const* text = R"( contract test { function f() { - uint128 a = 3; - int128 b = 4; - fixed c = b; - ufixed d = a; + fixed c = 3; + ufixed d = 4; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_rational_conversion) +BOOST_AUTO_TEST_CASE(fixed_type_rational_fraction_conversion) { char const* text = R"( contract test { function f() { - fixed c = 3; - ufixed d = 4; + fixed a = 4.5; + ufixed d = 2.5; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_literal) +BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed) { char const* text = R"( contract test { function f() { fixed a = 4.5; - ufixed d = 2.5; + int b = a; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_unary_operation) +{ + char const* text = R"( + contract test { + function f() { + ufixed8x16 a = +3.25; + fixed8x16 b = -3.25; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_literal_expression) +BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) +{ + char const* text = R"( + contract A { + function f() { + ufixed0x8 a = 0.5; + ufixed0x56 b = 0.0000000000000006661338147750939242541790008544921875; + fixed0x8 c = -0.5; + fixed0x56 d = -0.0000000000000006661338147750939242541790008544921875; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) { char const* text = R"( contract test { function f() { - ufixed8x248 a = 3.14 * 3; - ufixed8x248 b = 4 - 2.555555; - ufixed0x256 c = 1.0 / 3.0; - ufixed16x240 d = 599 + .5367; - ufixed8x248 e = 35.245 % 12.9; - ufixed8x248 f = 1.2 % 2; - fixed g = 2 ** -2; + ufixed248x8 a = 123456781234567979695948382928485849359686494864095409282048094275023098123.5; + ufixed0x256 b = 0.920890746623327805482905058466021565416131529487595827354393978494366605267637829135688384325135165352082715782143655824815685807141335814463015972119819459298455224338812271036061391763384038070334798471324635050876128428143374549108557403087615966796875; + ufixed0x256 c = 0.0000000000015198847363997979984922685411315294875958273543939784943666052676464653042434787697605517039455161817147718251801220885263595179331845639229818863564267318422845592626219390573301877339317935702714669975697814319204326238832436501979827880859375; + fixed248x8 d = -123456781234567979695948382928485849359686494864095409282048094275023098123.5; + fixed0x256 e = -0.93322335481643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125; + fixed0x256 g = -0.00011788606643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(rational_as_exponent_value) +BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size) { char const* text = R"( contract test { function f() { - fixed g = 2 ** -2.2; - fixed b = 3 ** 2.56; + ufixed a = 11/4; + ufixed248x8 b = a; } } )"; BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_invalid_size_conversion) +BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data) { char const* text = R"( contract test { function f() { - fixed a = 1/3; - ufixed248x8 b = a + 2.5; + ufixed0x256 a = 1/3; } } )"; BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(fixed_type_valid_size_conversion) +BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions) { char const* text = R"( contract test { function f() { - fixed a = 1/3; - ufixed248x8 b = ufixed248x8(a) + 2.5; + ufixed0x256 a = ufixed0x256(1/3); + ufixed0x248 b = ufixed0x248(1/3); + ufixed0x8 c = ufixed0x8(1/3); } } )"; - BOOST_CHECK(!success(text)); + BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(uint_array_declaration_with_fixed_type) +BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational) { char const* text = R"( contract test { function f() { - uint[fixed(3.56)] a; + uint[3.5] a; } } )"; BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(array_declaration_with_fixed_literal) +BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_fixed_type) { char const* text = R"( contract test { function f() { - uint[3.56] a; + uint[fixed(3.5)] a; } } )"; BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) +BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) { char const* text = R"( contract test { - mapping(ufixed8x248 => string) fixedString; function f() { - fixedString[3.14] = "Pi"; + bytes32 c = 3.2; } } )"; - BOOST_CHECK(success(text)); + BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(inline_array_fixed_type) +BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) { char const* text = R"( contract test { function f() { - fixed[3] memory a = [fixed(3.5), fixed(4.1234), fixed(967.32)]; + fixed a = 3.2; + bytes32 c = a; } } )"; - BOOST_CHECK(success(text)); + BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(inline_array_fixed_rationals) +BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) { char const* text = R"( contract test { + mapping(ufixed8x248 => string) fixedString; function f() { - ufixed8x248[4] memory a = [3.5, 4.1234, 2.5, 4.0]; + fixedString[0.5] = "Half"; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(zero_and_eight_variant_fixed) +BOOST_AUTO_TEST_CASE(fixed_points_inside_structs) { char const* text = R"( - contract A { - ufixed0x8 half = 0.5; + contract test { + struct myStruct { + ufixed a; + int b; + } + myStruct a = myStruct(3.125, 3); } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) +BOOST_AUTO_TEST_CASE(inline_array_fixed_types) { char const* text = R"( contract test { function f() { - ufixed0x256 a = 0.12345678; - ufixed24x8 b = 12345678.5; - ufixed0x256 c = 0.00000009; + fixed[3] memory a = [fixed(3.5), fixed(-4.25), fixed(967.125)]; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) +BOOST_AUTO_TEST_CASE(inline_array_rationals) { char const* text = R"( contract test { function f() { - var a = 0.12345678; - var b = 12345678.0; - var c = 0.00000009; + ufixed8x8[4] memory a = [3.5, 4.125, 2.5, 4.0]; } } )"; BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(invalid_rational_exponent_usage) +BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) { char const* text = R"( contract test { function f() { - fixed8x8 a = 3 ** 1.5; - fixed24x24 b = 2 ** (1/2); - fixed40x40 c = 42 ** (-1/4); - fixed48x48 d = 16 ** -0.33; + ufixed8x8 a = 3.5 * 3; + ufixed8x8 b = 4 - 2.5; + ufixed8x8 c = 11 / 4; + ufixed16x240 d = 599 + 0.21875; + ufixed8x248 e = ufixed8x248(35.245 % 12.9); + ufixed8x248 f = ufixed8x248(1.2 % 2); + fixed g = 2 ** -2; } } )"; - BOOST_CHECK(!success(text)); + BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) +BOOST_AUTO_TEST_CASE(rational_as_exponent_value) { char const* text = R"( contract test { function f() { - fixed a = 3 ** fixed(1.5); - fixed b = 2 ** fixed(1/2); - fixed c = 42 ** fixed(-1/4); - fixed d = 16 ** fixed(-0.33); + fixed g = 2 ** -2.2; + ufixed b = 3 ** 2.5; + ufixed24x24 b = 2 ** (1/2); + fixed40x40 c = 42 ** (-1/4); } } )"; - BOOST_CHECK(success(text)); + BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) +BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) { char const* text = R"( contract test { function f() { - bytes32 c = 3.183; + ufixed a = 3 ** ufixed(1.5); + ufixed b = 2 ** ufixed(1/2); + fixed c = 42 ** fixed(-1/4); + fixed d = 16 ** fixed(-0.33); } } )"; BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) +BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) { char const* text = R"( contract test { function f() { - fixed a = 3.183; - bytes32 c = a; + var a = 0.12345678; + var b = 12345678.352; + var c = 0.00000009; } } )"; - BOOST_CHECK(!success(text)); + BOOST_CHECK(success(text)); } -BOOST_AUTO_TEST_CASE(rational_unary_operation) +BOOST_AUTO_TEST_CASE(var_handle_divided_integers) { char const* text = R"( contract test { function f() { - ufixed8x248 a = +3.5134; - fixed8x248 b = -3.5134; + var x = 1/3; } } )"; - BOOST_CHECK(success(text)); + BOOST_CHECK(success(text)); } BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) @@ -3608,6 +3638,19 @@ BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) BOOST_CHECK(!success(text)); } +BOOST_AUTO_TEST_CASE(zero_handling) +{ + char const* text = R"( + contract test { + function f() { + fixed8x8 a = 0; + ufixed8x8 b = 0; + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3 From af354d75552b79144e771799b904e1c87931de90 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 10:46:25 +0200 Subject: Correctly handle unexpected exceptions during tests. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 36 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 48b58b71..d7f7961a 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -97,13 +97,20 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false) return make_pair(sourceUnit, std::make_shared(currentError->type())); } } + catch (InternalCompilerError const& _e) + { + string message("Internal compiler error"); + if (string const* description = boost::get_error_info(_e)) + message += ": " + *description; + BOOST_FAIL(message); + } catch (Error const& _e) { return make_pair(sourceUnit, std::make_shared(_e.type())); } - catch (Exception const& /*_exception*/) + catch (...) { - return make_pair(sourceUnit, nullptr); + BOOST_FAIL("Unexpected exception."); } return make_pair(sourceUnit, nullptr); } @@ -3516,6 +3523,19 @@ BOOST_AUTO_TEST_CASE(inline_array_rationals) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(rational_index_access) +{ + char const* text = R"( + contract test { + function f() { + uint[] memory a; + a[.5]; + } + } + )"; + BOOST_CHECK(!success(text)); +} + BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) { char const* text = R"( @@ -3578,6 +3598,18 @@ BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(var_and_rational_with_tuple) +{ + char const* text = R"( + contract test { + function f() { + var (a, b) = (.5, 1/3); + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(var_handle_divided_integers) { char const* text = R"( -- cgit v1.2.3 From 80c368dac1529994e736360812e2519e987e26b1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 10:26:53 +0200 Subject: Prefer mobileType() to check rational range. --- libsolidity/analysis/TypeChecker.cpp | 34 ++++++++++++++++++---------------- libsolidity/ast/Types.cpp | 13 ++++--------- libsolidity/ast/Types.h | 2 +- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 264fee6b..f28e08ab 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -772,12 +772,17 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { // Infer type from value. solAssert(!var.typeName(), ""); - if ( - valueComponentType->category() == Type::Category::RationalNumber && - !dynamic_pointer_cast(valueComponentType)->mobileType() - ) - fatalTypeError(_statement.initialValue()->location(), "Invalid rational " + valueComponentType->toString() + "."); var.annotation().type = valueComponentType->mobileType(); + if (!var.annotation().type) + if (valueComponentType->category() == Type::Category::RationalNumber) + fatalTypeError( + _statement.initialValue()->location(), + "Invalid rational " + + valueComponentType->toString() + + " (absolute value too large or divison by zero)." + ); + else + solAssert(false, ""); var.accept(*this); } else @@ -787,8 +792,8 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if ( valueComponentType->category() == Type::Category::RationalNumber && - dynamic_pointer_cast(valueComponentType)->denominator() != 1 && - !!valueComponentType->mobileType() + dynamic_cast(*valueComponentType).denominator() != 1 && + valueComponentType->mobileType() ) typeError( _statement.location(), @@ -818,10 +823,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) void TypeChecker::endVisit(ExpressionStatement const& _statement) { if (type(_statement.expression())->category() == Type::Category::RationalNumber) - if ( - !dynamic_pointer_cast(type(_statement.expression()))->integerType() && - !dynamic_pointer_cast(type(_statement.expression()))->fixedPointType() - ) + if (!dynamic_cast(*type(_statement.expression())).mobileType()) typeError(_statement.expression().location(), "Invalid rational number."); } @@ -1128,8 +1130,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (functionType->takesArbitraryParameters()) { if (auto t = dynamic_cast(argType.get())) - if (!t->integerType() && !t->fixedPointType()) - typeError(arguments[i]->location(), "Rational number too large."); + if (!t->mobileType()) + typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); } else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) typeError( @@ -1367,7 +1369,7 @@ bool TypeChecker::visit(IndexAccess const& _access) solAssert(!numberType->denominator() != 1 ,"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; @@ -1521,7 +1523,7 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte if ( type(_expression)->category() == Type::Category::RationalNumber && dynamic_pointer_cast(type(_expression))->denominator() != 1 && - !!type(_expression)->mobileType() + type(_expression)->mobileType() ) typeError( _expression.location(), @@ -1531,7 +1533,7 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte _expectedType.toString() + ". Try converting to type " + type(_expression)->mobileType()->toString() + - " or using an explicit conversion." + " or use an explicit conversion." ); else typeError( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 1dbb70e2..c2ebecb5 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -766,20 +766,15 @@ u256 RationalNumberType::literalValue(Literal const*) const TypePointer RationalNumberType::mobileType() const { 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; + return integerType(); + else + return fixedPointType(); } //TODO: combine integerType() and fixedPointType() into one function shared_ptr RationalNumberType::integerType() const { - solAssert(m_value.denominator() == 1, "Non integer type found."); + solAssert(m_value.denominator() == 1, "integerType() called for fractional number."); bigint value = integerPart(); bool negative = (value < 0); if (negative) // convert to positive number of same bit requirements diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 5e826684..c3095c97 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -205,7 +205,7 @@ public: 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 ConstantTypes and the pointer type - /// for storage reference types. + /// for storage reference types. Might return a null pointer if there is no fitting type. 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 /// given location. -- cgit v1.2.3 From 22318c0c1a67b9d7743e70d8f8378adbacb0079a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 11:15:41 +0200 Subject: Replace "unsigned int" by "unsigned". --- libsolidity/ast/Types.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c2ebecb5..92b2f6e8 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -123,8 +123,8 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) ); Token::Value token = _type.token(); - unsigned int m = _type.firstNumber(); - unsigned int n = _type.secondNumber(); + unsigned m = _type.firstNumber(); + unsigned n = _type.secondNumber(); switch (token) { @@ -699,19 +699,19 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ bigint newNumerator; if (other.m_value.denominator() != 1) return TypePointer(); - else if (abs(other.m_value) > numeric_limits::max()) + else if (abs(other.m_value) > numeric_limits::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()); - newNumerator = boost::multiprecision::pow(m_value.denominator(), absoluteValue.numerator().convert_to()); + newDenominator = boost::multiprecision::pow(m_value.numerator(), absoluteValue.numerator().convert_to()); + newNumerator = boost::multiprecision::pow(m_value.denominator(), absoluteValue.numerator().convert_to()); value = rational(newNumerator, newDenominator); } else { - newNumerator = boost::multiprecision::pow(m_value.numerator(), other.m_value.numerator().convert_to()); - newDenominator = boost::multiprecision::pow(m_value.denominator(), other.m_value.numerator().convert_to()); + newNumerator = boost::multiprecision::pow(m_value.numerator(), other.m_value.numerator().convert_to()); + newDenominator = boost::multiprecision::pow(m_value.denominator(), other.m_value.numerator().convert_to()); value = rational(newNumerator, newDenominator); } break; @@ -1555,9 +1555,9 @@ bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const return _convertTo.category() == category() || _convertTo.category() == Category::Integer; } -unsigned int EnumType::memberValue(ASTString const& _member) const +unsigned EnumType::memberValue(ASTString const& _member) const { - unsigned int index = 0; + unsigned index = 0; for (ASTPointer const& decl: m_enum.members()) { if (decl->name() == _member) -- cgit v1.2.3 From ef7049f9a6fd62d966493324b3f9365d28cabedf Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 12:42:38 +0200 Subject: Some cleanup. --- libsolidity/ast/Types.cpp | 90 ++++++++++++++++------------------------------- libsolidity/ast/Types.h | 2 +- 2 files changed, 32 insertions(+), 60 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 92b2f6e8..ad03bae4 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -186,12 +186,12 @@ TypePointer Type::forLiteral(Literal const& _literal) if (get<0>(validLiteral) == true) return make_shared(get<1>(validLiteral)); else - return TypePointer(); + return TypePointer(); } case Token::StringLiteral: return make_shared(_literal); default: - return shared_ptr(); + return TypePointer(); } } @@ -261,7 +261,7 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.category() == category()) - { + { IntegerType const& convertTo = dynamic_cast(_convertTo); if (convertTo.m_bits < m_bits) return false; @@ -471,12 +471,11 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal ) throw; //Only decimal notation allowed here, leading zeros would switch to octal. - auto leadingZeroes = find_if_not( + auto fractionalBegin = find_if_not( radixPoint + 1, _literal.value().end(), [](char const& a) { return a == '0'; } ); - auto fractionalBegin = leadingZeroes; denominator = bigint(string(fractionalBegin, _literal.value().end())); denominator /= boost::multiprecision::pow( @@ -550,13 +549,12 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const } else if (_convertTo.category() == Category::FixedPoint) { - //call fixed point type...call fractional bits...shift our number by the number of fractional bits... - //...see if it's a whole number. Make helper function for whether or not finitely representable. if (auto fixed = fixedPointType()) { + // We disallow implicit conversion if we would have to truncate (fixedPointType() + // can return a type that requires truncation). rational value = m_value * boost::multiprecision::pow(bigint(2), fixed->fractionalBits()); - if (value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo)) - return true; + return value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo); } return false; } @@ -573,13 +571,8 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (m_value.denominator() == 1) - { - TypePointer intType = integerType(); - return intType && intType->isExplicitlyConvertibleTo(_convertTo); - } - TypePointer fixType = fixedPointType(); - return fixType && fixType->isExplicitlyConvertibleTo(_convertTo); + TypePointer mobType = mobileType(); + return mobType && mobType->isExplicitlyConvertibleTo(_convertTo); } TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const @@ -608,19 +601,12 @@ TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) cons TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->category() == Category::Integer) - { - shared_ptr intType = integerType(); - if (!intType) - return TypePointer(); - return intType->binaryOperatorResult(_operator, _other); - } - else if (_other->category() == Category::FixedPoint) + if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) { - shared_ptr fixType = fixedPointType(); - if (!fixType) + auto mobile = mobileType(); + if (!mobile) return TypePointer(); - return fixType->binaryOperatorResult(_operator, _other); + return mobile->binaryOperatorResult(_operator, _other); } else if (_other->category() != category()) return TypePointer(); @@ -628,22 +614,14 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ RationalNumberType const& other = dynamic_cast(*_other); if (Token::isCompareOp(_operator)) { - if (m_value.denominator() == 1) - { - shared_ptr thisIntegerType = integerType(); - shared_ptr otherIntegerType = other.integerType(); - if (!thisIntegerType || !otherIntegerType) - return TypePointer(); - return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); - } - else - { - shared_ptr thisFixedPointType = fixedPointType(); - shared_ptr otherFixedPointType = other.fixedPointType(); - if (!thisFixedPointType || !otherFixedPointType) - return TypePointer(); - return thisFixedPointType->binaryOperatorResult(_operator, otherFixedPointType); - } + // Since we do not have a "BoolConstantType", we have to do the acutal comparison + // at runtime and convert to mobile typse first. Such a comparison is not a very common + // use-case and will be optimized away. + TypePointer thisMobile = mobileType(); + TypePointer otherMobile = other.mobileType(); + if (!thisMobile || !otherMobile) + return TypePointer(); + return thisMobile->binaryOperatorResult(_operator, otherMobile); } else { @@ -695,25 +673,19 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ break; case Token::Exp: { - bigint newDenominator; - bigint newNumerator; + using boost::multiprecision::pow; if (other.m_value.denominator() != 1) return TypePointer(); - else if (abs(other.m_value) > numeric_limits::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()); - newNumerator = boost::multiprecision::pow(m_value.denominator(), absoluteValue.numerator().convert_to()); - value = rational(newNumerator, newDenominator); - } + else if (abs(other.m_value) > numeric_limits::max()) + return TypePointer(); // This will need too much memory to represent. + uint32_t exponent = abs(other.m_value).numerator().convert_to(); + bigint numerator = pow(m_value.numerator(), exponent); + bigint denominator = pow(m_value.denominator(), exponent); + if (other.m_value >= 0) + value = rational(numerator, denominator); else - { - newNumerator = boost::multiprecision::pow(m_value.numerator(), other.m_value.numerator().convert_to()); - newDenominator = boost::multiprecision::pow(m_value.denominator(), other.m_value.numerator().convert_to()); - value = rational(newNumerator, newDenominator); - } + // invert + value = rational(denominator, numerator); break; } default: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c3095c97..441ab2ae 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -365,7 +365,7 @@ public: /// @returns true if the literal is a valid integer. static std::tuple isValidLiteral(Literal const& _literal); - explicit RationalNumberType(rational _value): + explicit RationalNumberType(rational const& _value): m_value(_value) {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; -- cgit v1.2.3 From 4dfe9a216cd9545e6246d68916a759b0187feab1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 12:43:05 +0200 Subject: Disallow explicit fixed->enum, but allow bytes->fixed. --- libsolidity/ast/Types.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index ad03bae4..c5bb77f6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -395,7 +395,6 @@ bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.category() == category() || _convertTo.category() == Category::Integer || - _convertTo.category() == Category::Enum || _convertTo.category() == Category::FixedBytes; } @@ -859,6 +858,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.category() == Category::Integer || + _convertTo.category() == Category::FixedPoint || _convertTo.category() == Category::Contract || _convertTo.category() == category(); } -- cgit v1.2.3 From 02e1c9be0d03f4dd576792dbab7c8f6abca86a5e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 13:31:10 +0200 Subject: Cleanup. --- libsolidity/ast/Types.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c5bb77f6..924ad3b7 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -552,7 +552,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { // We disallow implicit conversion if we would have to truncate (fixedPointType() // can return a type that requires truncation). - rational value = m_value * boost::multiprecision::pow(bigint(2), fixed->fractionalBits()); + rational value = m_value * (bigint(1) << fixed->fractionalBits()); return value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo); } return false; @@ -711,18 +711,23 @@ string RationalNumberType::toString(bool) const u256 RationalNumberType::literalValue(Literal const*) const { + // We ignore the literal and hope that the type was correctly determined to represent + // its value. + u256 value; bigint shiftedValue; - if (m_value.denominator() != 1) + if (m_value.denominator() == 1) + shiftedValue = m_value.numerator(); + else { - rational temporaryValue = m_value; auto fixed = fixedPointType(); - temporaryValue *= boost::multiprecision::pow(bigint(2), fixed->fractionalBits()); - shiftedValue = temporaryValue.numerator() / temporaryValue.denominator(); + solAssert(!!fixed, ""); + rational shifted = m_value * (bigint(1) << fixed->fractionalBits()); + // truncate + shiftedValue = shifted.numerator() / shifted.denominator(); } - else - shiftedValue = integerPart(); + // we ignore the literal and hope that the type was correctly determined solAssert(shiftedValue <= u256(-1), "Integer constant too large."); solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); @@ -742,7 +747,6 @@ TypePointer RationalNumberType::mobileType() const return fixedPointType(); } -//TODO: combine integerType() and fixedPointType() into one function shared_ptr RationalNumberType::integerType() const { solAssert(m_value.denominator() == 1, "integerType() called for fractional number."); @@ -762,13 +766,12 @@ shared_ptr RationalNumberType::integerType() const shared_ptr RationalNumberType::fixedPointType() const { bool negative = (m_value < 0); - bigint fillRationalBits = bigint(1) << 256; //use this because rationals don't have bit ops unsigned fractionalBits = 0; unsigned integerBits = 0; - rational value = abs(m_value); //convert to absolute value of same type for byte requirements + rational value = abs(m_value); // We care about the sign later. rational maxValue = negative ? - rational(fillRationalBits) / 2: - rational(fillRationalBits) - 1; + rational(bigint(1) << 255): + rational((bigint(1) << 256) - 1); while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256) { @@ -781,7 +784,9 @@ shared_ptr RationalNumberType::fixedPointType() const // u256(v) is the actual value that will be put on the stack // From here on, very similar to integerType() bigint v = value.numerator() / value.denominator(); - if (negative) //convert back to negative number and then shift into a positive number of equal size + if (negative) + // modify value to satisfy bit requirements for negative numbers: + // add one bit for sign and decrement because negative numbers can be larger v = (v - 1) << 1; if (v > u256(-1)) @@ -793,7 +798,7 @@ shared_ptr RationalNumberType::fixedPointType() const if (integerBits > 256 || fractionalBits > 256 || fractionalBits + integerBits > 256) return shared_ptr(); - if (integerBits + fractionalBits == 0) + if (integerBits == 0 && fractionalBits == 0) { integerBits = 0; fractionalBits = 8; -- cgit v1.2.3 From cf226f0607386d1e6d9c75ebc7ce24e733ca4315 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 14:07:11 +0200 Subject: Special case for moving sign bit to fractional part. --- libsolidity/ast/Types.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 924ad3b7..31248fb6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -767,7 +767,6 @@ shared_ptr RationalNumberType::fixedPointType() const { bool negative = (m_value < 0); unsigned fractionalBits = 0; - unsigned integerBits = 0; rational value = abs(m_value); // We care about the sign later. rational maxValue = negative ? rational(bigint(1) << 255): @@ -788,13 +787,19 @@ shared_ptr RationalNumberType::fixedPointType() const // modify value to satisfy bit requirements for negative numbers: // add one bit for sign and decrement because negative numbers can be larger v = (v - 1) << 1; - + if (v > u256(-1)) return shared_ptr(); - if (0 == integerPart()) + + unsigned totalBits = bytesRequired(v) * 8; + solAssert(totalBits <= 256, ""); + unsigned integerBits = totalBits >= fractionalBits ? totalBits - fractionalBits : 0; + // Special case: Numbers between -1 and 0 have their sign bit in the fractional part. + if (negative && abs(m_value) < 1 && totalBits > fractionalBits) + { + fractionalBits += 8; integerBits = 0; - else - integerBits = (bytesRequired(v) * 8) - fractionalBits; + } if (integerBits > 256 || fractionalBits > 256 || fractionalBits + integerBits > 256) return shared_ptr(); -- cgit v1.2.3 From 656405240e08e47fce40a2f62af93abc758bd2d2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 14:30:24 +0200 Subject: Simplify interface of RationalNumber. --- libsolidity/analysis/ReferencesResolver.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 10 +++++----- libsolidity/ast/Types.cpp | 24 ++++++++++++------------ libsolidity/ast/Types.h | 9 +++++---- libsolidity/formal/Why3Translator.cpp | 4 ++-- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 9f83971b..a7b9e8b8 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -104,7 +104,7 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) if (!length->annotation().type) ConstantEvaluator e(*length); auto const* lengthType = dynamic_cast(length->annotation().type.get()); - if (!lengthType || lengthType->denominator() != 1) + if (!lengthType || lengthType->isFractional()) fatalTypeError(length->location(), "Invalid array length, expected integer literal."); else _typeName.annotation().type = make_shared(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f28e08ab..6df9aae0 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -792,7 +792,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if ( valueComponentType->category() == Type::Category::RationalNumber && - dynamic_cast(*valueComponentType).denominator() != 1 && + dynamic_cast(*valueComponentType).isFractional() && valueComponentType->mobileType() ) typeError( @@ -1366,9 +1366,9 @@ bool TypeChecker::visit(IndexAccess const& _access) expectType(*index, IntegerType(256)); if (auto numberType = dynamic_cast(type(*index).get())) { - solAssert(!numberType->denominator() != 1 ,"Invalid type for array access."); - if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) - typeError(_access.location(), "Out of bounds array access."); + if (!numberType->isFractional()) // error is reported above + if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) + typeError(_access.location(), "Out of bounds array access."); } } resultType = actualType.baseType(); @@ -1522,7 +1522,7 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte { if ( type(_expression)->category() == Type::Category::RationalNumber && - dynamic_pointer_cast(type(_expression))->denominator() != 1 && + dynamic_pointer_cast(type(_expression))->isFractional() && type(_expression)->mobileType() ) typeError( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 31248fb6..7995e7e3 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -534,15 +534,15 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const auto targetType = dynamic_cast(&_convertTo); if (m_value == 0) return true; - if (m_value.denominator() != 1) + if (isFractional()) return false; int forSignBit = (targetType->isSigned() ? 1 : 0); if (m_value > 0) { - if (integerPart() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + if (m_value.numerator() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) return true; } - else if (targetType->isSigned() && -integerPart() <= (u256(1) << (targetType->numBits() - forSignBit))) + else if (targetType->isSigned() && -m_value.numerator() <= (u256(1) << (targetType->numBits() - forSignBit))) return true; return false; } @@ -560,7 +560,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const else if (_convertTo.category() == Category::FixedBytes) { FixedBytesType const& fixedBytes = dynamic_cast(_convertTo); - if (m_value.denominator() == 1) + if (!isFractional()) return fixedBytes.numBytes() * 8 >= integerType()->numBits(); else return false; @@ -580,7 +580,7 @@ TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) cons switch (_operator) { case Token::BitNot: - if(m_value.denominator() != 1) + if (isFractional()) return TypePointer(); value = ~m_value.numerator(); break; @@ -625,7 +625,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ else { rational value; - bool fractional = (m_value.denominator() != 1 || other.m_value.denominator() != 1); + bool fractional = isFractional() || other.isFractional(); switch (_operator) { //bit operations will only be enabled for integers and fixed types that resemble integers @@ -673,7 +673,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ case Token::Exp: { using boost::multiprecision::pow; - if (other.m_value.denominator() != 1) + if (other.isFractional()) return TypePointer(); else if (abs(other.m_value) > numeric_limits::max()) return TypePointer(); // This will need too much memory to represent. @@ -704,7 +704,7 @@ bool RationalNumberType::operator==(Type const& _other) const string RationalNumberType::toString(bool) const { - if (m_value.denominator() == 1) + if (!isFractional()) return "int_const " + m_value.numerator().str(); return "rational_const " + m_value.numerator().str() + '/' + m_value.denominator().str(); } @@ -717,7 +717,7 @@ u256 RationalNumberType::literalValue(Literal const*) const u256 value; bigint shiftedValue; - if (m_value.denominator() == 1) + if (!isFractional()) shiftedValue = m_value.numerator(); else { @@ -741,7 +741,7 @@ u256 RationalNumberType::literalValue(Literal const*) const TypePointer RationalNumberType::mobileType() const { - if (m_value.denominator() == 1) + if (!isFractional()) return integerType(); else return fixedPointType(); @@ -749,8 +749,8 @@ TypePointer RationalNumberType::mobileType() const shared_ptr RationalNumberType::integerType() const { - solAssert(m_value.denominator() == 1, "integerType() called for fractional number."); - bigint value = integerPart(); + solAssert(!isFractional(), "integerType() called for fractional number."); + bigint value = m_value.numerator(); bool negative = (value < 0); if (negative) // convert to positive number of same bit requirements value = ((0 - value) - 1) << 1; diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 441ab2ae..26c0f902 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -204,8 +204,9 @@ 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 ConstantTypes and the pointer type - /// for storage reference types. Might return a null pointer if there is no fitting type. + /// This returns the corresponding IntegerType or FixedPointType for RationalNumberType + /// and the pointer type for storage reference types. + /// Might return a null pointer if there is no fitting type. 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 /// given location. @@ -388,8 +389,8 @@ public: /// If the integer part does not fit, returns an empty pointer. std::shared_ptr fixedPointType() const; - bigint denominator() const { return m_value.denominator(); } - bigint integerPart() const { return m_value.numerator() / m_value.denominator(); } + /// @returns true iff the value is not an integer. + bool isFractional() const { return m_value.denominator() != 1; } private: rational m_value; diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index 5921b9a4..c794cb24 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -431,7 +431,7 @@ bool Why3Translator::visit(BinaryOperation const& _binaryOperation) if (commonType.category() == Type::Category::RationalNumber) { auto const& constantNumber = dynamic_cast(commonType); - if (constantNumber.denominator() != bigint(1)) + if (constantNumber.isFractional()) error(_binaryOperation, "Fractional numbers not supported."); add("(of_int " + toString(commonType.literalValue(nullptr)) + ")"); return false; @@ -595,7 +595,7 @@ bool Why3Translator::visit(Literal const& _literal) case Type::Category::RationalNumber: { auto const& constantNumber = dynamic_cast(*type); - if (constantNumber.denominator() != 1) + if (constantNumber.isFractional()) error(_literal, "Fractional numbers not supported."); add("(of_int " + toString(type->literalValue(&_literal)) + ")"); break; -- cgit v1.2.3 From d4206b7cd0bb0b8a3364c29fe097db035f308388 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 10 May 2016 14:57:29 +0200 Subject: Remove unused tests and add asserts for not implemented parts in code generation. quick fix on christian's rational change so that ubuntu will stop yelling be more specific with rational declaration for Windows sake rational in namespace correction for windows --- libsolidity/analysis/TypeChecker.cpp | 2 ++ libsolidity/ast/Types.cpp | 5 ++--- libsolidity/ast/Types.h | 4 ++-- libsolidity/codegen/CompilerUtils.cpp | 7 ++++--- libsolidity/codegen/ExpressionCompiler.cpp | 7 +++---- libsolidity/codegen/LValue.cpp | 9 ++++++--- test/libsolidity/SolidityEndToEndTest.cpp | 22 ---------------------- 7 files changed, 19 insertions(+), 37 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 6df9aae0..5ae0443a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -774,6 +774,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) solAssert(!var.typeName(), ""); var.annotation().type = valueComponentType->mobileType(); if (!var.annotation().type) + { if (valueComponentType->category() == Type::Category::RationalNumber) fatalTypeError( _statement.initialValue()->location(), @@ -783,6 +784,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) ); else solAssert(false, ""); + } var.accept(*this); } else diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 7995e7e3..0df68d3d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -32,7 +32,6 @@ using namespace std; using namespace dev; using namespace dev::solidity; -using rational = boost::rational; void StorageOffsets::computeOffsets(TypePointers const& _types) { @@ -769,8 +768,8 @@ shared_ptr RationalNumberType::fixedPointType() const unsigned fractionalBits = 0; rational value = abs(m_value); // We care about the sign later. rational maxValue = negative ? - rational(bigint(1) << 255): - rational((bigint(1) << 256) - 1); + rational(bigint(1) << 255, 1): + rational((bigint(1) << 256) - 1, 1); while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 26c0f902..0c437316 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -44,7 +44,7 @@ class FunctionType; // forward using TypePointer = std::shared_ptr; using FunctionTypePointer = std::shared_ptr; using TypePointers = std::vector; -using rational = boost::rational; +using rational = boost::rational; enum class DataLocation { Storage, CallData, Memory }; @@ -389,7 +389,7 @@ public: /// If the integer part does not fit, returns an empty pointer. std::shared_ptr fixedPointType() const; - /// @returns true iff the value is not an integer. + /// @returns true if the value is not an integer. bool isFractional() const { return m_value.denominator() != 1; } private: diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 8589900e..e35f3374 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -343,10 +343,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp case Type::Category::Enum: solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, ""); break; + case Type::Category::FixedPoint: + solAssert(false, "Not yet implemented - FixedPointType."); case Type::Category::Integer: case Type::Category::Contract: case Type::Category::RationalNumber: - case Type::Category::FixedPoint: if (targetTypeCategory == Type::Category::FixedBytes) { solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::RationalNumber, @@ -375,8 +376,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp if (auto typeOnStack = dynamic_cast(&_typeOnStack)) if (targetFixedPointType.integerBits() > typeOnStack->numBits()) cleanHigherOrderBits(*typeOnStack); - //need m_context call here - + solAssert(false, "Not yet implemented - FixedPointType."); } else { @@ -389,6 +389,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp RationalNumberType const& constType = dynamic_cast(_typeOnStack); // We know that the stack is clean, we only have to clean for a narrowing conversion // where cleanup is forced. + solAssert(!constType.isFractional(), "Not yet implemented - FixedPointType."); if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded) cleanHigherOrderBits(targetType); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index baf587f0..a01e306e 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -281,10 +281,6 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) { CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation); - //@todo type checking and creating code for an operator should be in the same place: - // 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::RationalNumber) { m_context << _unaryOperation.annotation().type->literalValue(nullptr); @@ -1306,6 +1302,9 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty IntegerType const& type = dynamic_cast(_type); bool const c_isSigned = type.isSigned(); + if (_type.category() == Type::Category::FixedPoint) + solAssert(false, "Not yet implemented - FixedPointType."); + switch (_operator) { case Token::Add: diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 1d1956aa..ea8bc1ba 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -179,6 +179,9 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const m_context << Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1 << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; + if (m_dataType->category() == Type::Category::FixedPoint) + // implementation should be very similar to the integer case. + solAssert(false, "Not yet implemented - FixedPointType."); if (m_dataType->category() == Type::Category::FixedBytes) m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL; else if ( @@ -186,7 +189,6 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const dynamic_cast(*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,9 +242,10 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc << Instruction::DUP2 << Instruction::MUL << Instruction::DIV; + else if (m_dataType->category() == Type::Category::FixedPoint) + // implementation should be very similar to the integer case. + solAssert(false, "Not yet implemented - FixedPointType."); 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 // stack: value storage_ref updated_value m_context << Instruction::SWAP1 << Instruction::SSTORE; if (_move) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 67748c1f..de428f96 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6634,28 +6634,6 @@ BOOST_AUTO_TEST_CASE(delete_on_array_of_structs) } -/*BOOST_AUTO_TEST_CASE(fixed_data_type) -{ - char const* sourceCode = R"( - contract C { - fixed public pi = 3.141592; - } - )"; - compileAndRun(sourceCode, 0, "C"); -} - -BOOST_AUTO_TEST_CASE(fixed_data_type_expression) -{ - char const* sourceCode = R"( - contract C { - function f(fixed a) returns (fixed) { - return (a + 3); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); -}*/ - BOOST_AUTO_TEST_CASE(internal_library_function) { // tests that internal library functions can be called from outside -- cgit v1.2.3