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