aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2016-05-12 18:30:10 +0800
committerchriseth <c@ethdev.com>2016-05-12 18:30:10 +0800
commit1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0 (patch)
tree267d5df3091710f7a1c3b54ddb8fcac5ab794015
parent9e36bdda8a9552f1885e0a63a85db588623b39b2 (diff)
parentd4206b7cd0bb0b8a3364c29fe097db035f308388 (diff)
downloaddexon-solidity-1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0.tar
dexon-solidity-1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0.tar.gz
dexon-solidity-1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0.tar.bz2
dexon-solidity-1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0.tar.lz
dexon-solidity-1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0.tar.xz
dexon-solidity-1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0.tar.zst
dexon-solidity-1ab0f25dffe19f2cc9e1a0e2fa03a1091679f5a0.zip
Merge pull request #402 from VoR0220/fixedDataType
Fixed Type initial PR
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp6
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp7
-rw-r--r--libsolidity/analysis/TypeChecker.cpp113
-rw-r--r--libsolidity/ast/Types.cpp477
-rw-r--r--libsolidity/ast/Types.h76
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp26
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp15
-rw-r--r--libsolidity/codegen/LValue.cpp6
-rw-r--r--libsolidity/formal/Why3Translator.cpp12
-rw-r--r--libsolidity/parsing/Token.cpp5
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp1
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp467
-rw-r--r--test/libsolidity/SolidityParser.cpp46
13 files changed, 1047 insertions, 210 deletions
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
index 6beb655e..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<IntegerConstantType const*>(subType.get()))
+ if (!dynamic_cast<RationalNumberType 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<RationalNumberType const*>(leftType.get()))
BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression."));
- if (!dynamic_cast<IntegerConstantType const*>(rightType.get()))
+ if (!dynamic_cast<RationalNumberType 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..a7b9e8b8 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -103,10 +103,9 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
{
if (!length->annotation().type)
ConstantEvaluator e(*length);
-
- auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get());
- if (!lengthType)
- fatalTypeError(length->location(), "Invalid array length.");
+ auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get());
+ if (!lengthType || lengthType->isFractional())
+ fatalTypeError(length->location(), "Invalid array length, expected integer literal.");
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..5ae0443a 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -772,26 +772,51 @@ 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()
- )
- fatalTypeError(_statement.initialValue()->location(), "Invalid integer constant " + 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
{
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_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
+ 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;
@@ -799,9 +824,9 @@ 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())
- typeError(_statement.expression().location(), "Invalid integer constant.");
+ if (type(_statement.expression())->category() == Type::Category::RationalNumber)
+ if (!dynamic_cast<RationalNumberType const&>(*type(_statement.expression())).mobileType())
+ typeError(_statement.expression().location(), "Invalid rational number.");
}
bool TypeChecker::visit(Conditional const& _conditional)
@@ -1106,9 +1131,9 @@ 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 (!t->integerType())
- typeError(arguments[i]->location(), "Integer constant too large.");
+ if (auto t = dynamic_cast<RationalNumberType const*>(argType.get()))
+ 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(
@@ -1341,9 +1366,12 @@ 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))
- typeError(_access.location(), "Out of bounds array access.");
+ if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get()))
+ {
+ 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();
isLValue = actualType.location() != DataLocation::CallData;
@@ -1367,8 +1395,8 @@ bool TypeChecker::visit(IndexAccess const& _access)
resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType()));
else
{
- index->accept(*this);
- if (auto length = dynamic_cast<IntegerConstantType const*>(type(*index).get()))
+ expectType(*index, IntegerType(256));
+ if (auto length = dynamic_cast<RationalNumberType const*>(type(*index).get()))
resultType = make_shared<TypeType>(make_shared<ArrayType>(
DataLocation::Memory,
typeType.actualType(),
@@ -1387,7 +1415,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<RationalNumberType const*>(type(*index).get()))
if (bytesType.numBytes() <= integerType->literalValue(nullptr))
typeError(_access.location(), "Out of bounds array access.");
}
@@ -1492,16 +1520,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<RationalNumberType const>(type(_expression))->isFractional() &&
+ 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 use 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 d541de23..0df68d3d 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -122,7 +122,8 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
);
Token::Value token = _type.token();
- unsigned int m = _type.firstNumber();
+ unsigned m = _type.firstNumber();
+ unsigned n = _type.secondNumber();
switch (token)
{
@@ -132,10 +133,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,13 +180,17 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::Number:
- if (!IntegerConstantType::isValidLiteral(_literal))
+ {
+ tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal);
+ if (get<0>(validLiteral) == true)
+ return make_shared<RationalNumberType>(get<1>(validLiteral));
+ else
return TypePointer();
- return make_shared<IntegerConstantType>(_literal);
+ }
case Token::StringLiteral:
return make_shared<StringLiteralType>(_literal);
default:
- return shared_ptr<Type>();
+ return TypePointer();
}
}
@@ -246,17 +259,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 || isAddress())
+ 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 +328,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::RationalNumber && _other->category() != category())
return TypePointer();
auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other));
@@ -335,143 +361,287 @@ 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)
{
- try
+ 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())
{
- bigint x(_literal.value());
+ 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);
}
- catch (...)
- {
+
+ return false;
+}
+
+bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ return _convertTo.category() == category() ||
+ _convertTo.category() == Category::Integer ||
+ _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;
- }
- return true;
+ 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);
}
-IntegerConstantType::IntegerConstantType(Literal const& _literal)
+TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
- m_value = bigint(_literal.value());
+ if (
+ _other->category() != Category::RationalNumber
+ && _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::isBitOp(_operator) || Token::isBooleanOp(_operator))
+ return TypePointer();
+ if (Token::Exp == _operator)
+ return TypePointer();
+ return commonType;
+}
+
+tuple<bool, rational> 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())
+ {
+ 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 fractionalBegin = find_if_not(
+ radixPoint + 1,
+ _literal.value().end(),
+ [](char const& a) { return a == '0'; }
+ );
+ 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));
+ x = numerator + denominator;
+ }
+ else
+ x = bigint(_literal.value());
+ }
+ catch (...)
+ {
+ return make_tuple(false, rational(0));
+ }
switch (_literal.subDenomination())
{
- case Literal::SubDenomination::Wei:
- case Literal::SubDenomination::Second:
- case Literal::SubDenomination::None:
- 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;
+ 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);
}
-bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+bool RationalNumberType::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;
+ if (isFractional())
+ return false;
int forSignBit = (targetType->isSigned() ? 1 : 0);
if (m_value > 0)
{
- if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)))
+ if (m_value.numerator() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)))
return true;
}
- else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit)))
+ else if (targetType->isSigned() && -m_value.numerator() <= (u256(1) << (targetType->numBits() - forSignBit)))
return true;
return false;
}
+ else if (_convertTo.category() == Category::FixedPoint)
+ {
+ 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 * (bigint(1) << fixed->fractionalBits());
+ return value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo);
+ }
+ return false;
+ }
else if (_convertTo.category() == Category::FixedBytes)
{
FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
- return fixedBytes.numBytes() * 8 >= integerType()->numBits();
+ if (!isFractional())
+ return fixedBytes.numBytes() * 8 >= integerType()->numBits();
+ else
+ return false;
}
- else
- return false;
+ return false;
}
-bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- TypePointer intType = integerType();
- return intType && intType->isExplicitlyConvertibleTo(_convertTo);
+ TypePointer mobType = mobileType();
+ return mobType && mobType->isExplicitlyConvertibleTo(_convertTo);
}
-TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const
+TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const
{
- bigint value;
+ rational value;
switch (_operator)
{
case Token::BitNot:
- value = ~m_value;
+ if (isFractional())
+ return TypePointer();
+ 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();
default:
return TypePointer();
}
- return make_shared<IntegerConstantType>(value);
+ return make_shared<RationalNumberType>(value);
}
-TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
- if (_other->category() == Category::Integer)
+ if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{
- shared_ptr<IntegerType const> intType = integerType();
- if (!intType)
+ auto mobile = mobileType();
+ if (!mobile)
return TypePointer();
- return intType->binaryOperatorResult(_operator, _other);
+ return mobile->binaryOperatorResult(_operator, _other);
}
else if (_other->category() != category())
return TypePointer();
- IntegerConstantType const& other = dynamic_cast<IntegerConstantType const&>(*_other);
+ RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other);
if (Token::isCompareOp(_operator))
{
- shared_ptr<IntegerType const> thisIntegerType = integerType();
- shared_ptr<IntegerType const> otherIntegerType = other.integerType();
- if (!thisIntegerType || !otherIntegerType)
+ // 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 thisIntegerType->binaryOperatorResult(_operator, otherIntegerType);
+ return thisMobile->binaryOperatorResult(_operator, otherMobile);
}
else
{
- bigint value;
+ rational value;
+ bool fractional = isFractional() || other.isFractional();
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 (fractional)
+ return TypePointer();
+ value = m_value.numerator() | other.m_value.numerator();
break;
case Token::BitXor:
- value = m_value ^ other.m_value;
+ if (fractional)
+ return TypePointer();
+ value = m_value.numerator() ^ other.m_value.numerator();
break;
case Token::BitAnd:
- value = m_value & other.m_value;
+ if (fractional)
+ return TypePointer();
+ value = m_value.numerator() & other.m_value.numerator();
break;
case Token::Add:
value = m_value + other.m_value;
@@ -485,68 +655,104 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty
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 (fractional)
+ {
+ 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();
+ break;
case Token::Exp:
- if (other.m_value < 0)
- return TypePointer();
- else if (other.m_value > numeric_limits<unsigned int>::max())
+ {
+ using boost::multiprecision::pow;
+ if (other.isFractional())
return TypePointer();
+ else if (abs(other.m_value) > numeric_limits<uint32_t>::max())
+ return TypePointer(); // This will need too much memory to represent.
+ uint32_t exponent = abs(other.m_value).numerator().convert_to<uint32_t>();
+ bigint numerator = pow(m_value.numerator(), exponent);
+ bigint denominator = pow(m_value.denominator(), exponent);
+ if (other.m_value >= 0)
+ value = rational(numerator, denominator);
else
- value = boost::multiprecision::pow(m_value, other.m_value.convert_to<unsigned int>());
+ // invert
+ value = rational(denominator, numerator);
break;
+ }
default:
return TypePointer();
}
- return make_shared<IntegerConstantType>(value);
+ return make_shared<RationalNumberType>(value);
}
}
-bool IntegerConstantType::operator==(Type const& _other) const
+bool RationalNumberType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
- return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value;
+ RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(_other);
+ return m_value == other.m_value;
}
-string IntegerConstantType::toString(bool) const
+string RationalNumberType::toString(bool) const
{
- return "int_const " + m_value.str();
+ if (!isFractional())
+ 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 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 (!isFractional())
+ shiftedValue = m_value.numerator();
+ else
+ {
+ auto fixed = fixedPointType();
+ solAssert(!!fixed, "");
+ rational shifted = m_value * (bigint(1) << fixed->fractionalBits());
+ // truncate
+ shiftedValue = shifted.numerator() / shifted.denominator();
+ }
+
// 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(shiftedValue <= u256(-1), "Integer constant too large.");
+ solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small.");
if (m_value >= 0)
- value = u256(m_value);
+ value = u256(shiftedValue);
else
- value = s2u(s256(m_value));
-
+ value = s2u(s256(shiftedValue));
return value;
}
-TypePointer IntegerConstantType::mobileType() const
+TypePointer RationalNumberType::mobileType() const
{
- auto intType = integerType();
- solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false));
- return intType;
+ if (!isFractional())
+ return integerType();
+ else
+ return fixedPointType();
}
-shared_ptr<IntegerType const> IntegerConstantType::integerType() const
+shared_ptr<IntegerType const> RationalNumberType::integerType() const
{
- bigint value = m_value;
+ 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 = ((-value) - 1) << 1;
+ value = ((0 - value) - 1) << 1;
if (value > u256(-1))
return shared_ptr<IntegerType const>();
else
@@ -556,6 +762,58 @@ shared_ptr<IntegerType const> IntegerConstantType::integerType() const
);
}
+shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
+{
+ bool negative = (m_value < 0);
+ unsigned fractionalBits = 0;
+ rational value = abs(m_value); // We care about the sign later.
+ rational maxValue = negative ?
+ rational(bigint(1) << 255, 1):
+ rational((bigint(1) << 256) - 1, 1);
+
+ while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256)
+ {
+ value *= 0x100;
+ fractionalBits += 8;
+ }
+
+ if (value > maxValue)
+ return shared_ptr<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)
+ // 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<FixedPointType const>();
+
+ 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;
+ }
+
+ if (integerBits > 256 || fractionalBits > 256 || fractionalBits + integerBits > 256)
+ return shared_ptr<FixedPointType const>();
+ if (integerBits == 0 && fractionalBits == 0)
+ {
+ integerBits = 0;
+ fractionalBits = 8;
+ }
+
+ return make_shared<FixedPointType>(
+ integerBits, fractionalBits,
+ negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned
+ );
+}
+
StringLiteralType::StringLiteralType(Literal const& _literal):
m_value(_literal.value())
{
@@ -609,6 +867,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();
}
@@ -1277,9 +1536,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<EnumValue> const& decl: m_enum.members())
{
if (decl->name() == _member)
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index d42bb5dd..0c437316 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<dev::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, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
FixedBytes, Contract, Struct, Function, Enum, Tuple,
Mapping, TypeType, Modifier, Magic, Module
};
@@ -202,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 IntegerConstantTypes and the pointer type
- /// for storage reference types.
+ /// 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.
@@ -309,20 +312,63 @@ private:
};
/**
- * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10.
- * There is one distinct type per value.
+ * A fixed point type number (signed, unsigned).
*/
-class IntegerConstantType: public Type
+class FixedPointType: public Type
{
public:
- virtual Category category() const override { return Category::IntegerConstant; }
+ enum class Modifier
+ {
+ Unsigned, Signed
+ };
+ virtual Category category() const override { return Category::FixedPoint; }
- /// @returns true if the literal is a valid integer.
- static bool isValidLiteral(Literal const& _literal);
+ 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(); }
- explicit IntegerConstantType(Literal const& _literal);
- explicit IntegerConstantType(bigint _value): m_value(_value) {}
+ 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 RationalNumberType: public Type
+{
+public:
+
+ virtual Category category() const override { return Category::RationalNumber; }
+
+ /// @returns true if the literal is a valid integer.
+ static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
+
+ explicit RationalNumberType(rational const& _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,15 @@ 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 incurs the least precision loss.
+ /// If the integer part does not fit, returns an empty pointer.
+ std::shared_ptr<FixedPointType const> fixedPointType() const;
+
+ /// @returns true if the value is not an integer.
+ bool isFractional() const { return m_value.denominator() != 1; }
private:
- bigint m_value;
+ rational m_value;
};
/**
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 36ed480e..e35f3374 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -343,12 +343,14 @@ 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::IntegerConstant:
+ case Type::Category::RationalNumber:
if (targetTypeCategory == Type::Category::FixedBytes)
{
- solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant,
+ solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::RationalNumber,
"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,17 +363,33 @@ 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::RationalNumber ||
+ 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);
+ solAssert(false, "Not yet implemented - FixedPointType.");
+ }
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::RationalNumber)
{
- IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
+ RationalNumberType const& constType = dynamic_cast<RationalNumberType const&>(_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 ab02e0b5..a01e306e 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -281,11 +281,7 @@ 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::IntegerConstant)
+ if (_unaryOperation.annotation().type->category() == Type::Category::RationalNumber)
{
m_context << _unaryOperation.annotation().type->literalValue(nullptr);
return false;
@@ -360,7 +356,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::RationalNumber)
m_context << commonType.literalValue(nullptr);
else
{
@@ -370,7 +366,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::RationalNumber;
};
bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
if (swap)
@@ -1225,7 +1221,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
switch (type->category())
{
- case Type::Category::IntegerConstant:
+ case Type::Category::RationalNumber:
case Type::Category::Bool:
m_context << type->literalValue(&_literal);
break;
@@ -1306,6 +1302,9 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
IntegerType const& type = dynamic_cast<IntegerType const&>(_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 fcadd2ff..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 (
@@ -239,6 +242,9 @@ 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;
// stack: value storage_ref updated_value
m_context << Instruction::SWAP1 << Instruction::SSTORE;
diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp
index 24fbab13..c794cb24 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::RationalNumber)
{
+ auto const& constantNumber = dynamic_cast<RationalNumberType const&>(commonType);
+ if (constantNumber.isFractional())
+ 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::RationalNumber:
+ {
+ auto const& constantNumber = dynamic_cast<RationalNumberType const&>(*type);
+ if (constantNumber.isFractional())
+ 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..0ab97988 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);
@@ -156,7 +158,7 @@ tuple<Token::Value, unsigned int, unsigned int> 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 &&
@@ -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..de428f96 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -6631,6 +6631,7 @@ 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(internal_library_function)
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 9c411781..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<Error::Type const>(currentError->type()));
}
}
+ catch (InternalCompilerError const& _e)
+ {
+ string message("Internal compiler error");
+ if (string const* description = boost::get_error_info<errinfo_comment>(_e))
+ message += ": " + *description;
+ BOOST_FAIL(message);
+ }
catch (Error const& _e)
{
return make_pair(sourceUnit, std::make_shared<Error::Type const>(_e.type()));
}
- catch (Exception const& /*_exception*/)
+ catch (...)
{
- return make_pair(sourceUnit, nullptr);
+ BOOST_FAIL("Unexpected exception.");
}
return make_pair(sourceUnit, nullptr);
}
@@ -1330,15 +1337,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 +2227,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 +2778,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 +3216,44 @@ 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 +3285,404 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_type_long)
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_rational_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_rational_fraction_conversion)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ fixed a = 4.5;
+ ufixed d = 2.5;
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ fixed a = 4.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(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() {
+ 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(fixed_type_invalid_implicit_conversion_size)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ ufixed a = 11/4;
+ ufixed248x8 b = a;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ ufixed0x256 a = 1/3;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ ufixed0x256 a = ufixed0x256(1/3);
+ ufixed0x248 b = ufixed0x248(1/3);
+ ufixed0x8 c = ufixed0x8(1/3);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ uint[3.5] a;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_fixed_type)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ uint[fixed(3.5)] a;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ bytes32 c = 3.2;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ fixed a = 3.2;
+ bytes32 c = a;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal)
+{
+ char const* text = R"(
+ contract test {
+ mapping(ufixed8x248 => string) fixedString;
+ function f() {
+ fixedString[0.5] = "Half";
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_points_inside_structs)
+{
+ char const* text = R"(
+ contract test {
+ struct myStruct {
+ ufixed a;
+ int b;
+ }
+ myStruct a = myStruct(3.125, 3);
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_fixed_types)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ fixed[3] memory a = [fixed(3.5), fixed(-4.25), fixed(967.125)];
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_rationals)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ ufixed8x8[4] memory a = [3.5, 4.125, 2.5, 4.0];
+ }
+ }
+ )";
+ 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"(
+ contract test {
+ function f() {
+ 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_AUTO_TEST_CASE(rational_as_exponent_value)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ 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_AUTO_TEST_CASE(fixed_point_casting_exponents)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ 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(var_capable_of_holding_constant_rationals)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ var a = 0.12345678;
+ var b = 12345678.352;
+ var c = 0.00000009;
+ }
+ }
+ )";
+ 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"(
+ contract test {
+ function f() {
+ var x = 1/3;
+ }
+ }
+ )";
+ 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(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()
}
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()
}