aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp6
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp5
-rw-r--r--libsolidity/analysis/TypeChecker.cpp22
-rw-r--r--libsolidity/ast/Types.cpp472
-rw-r--r--libsolidity/ast/Types.h69
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp38
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp8
-rw-r--r--libsolidity/codegen/LValue.cpp4
-rw-r--r--libsolidity/formal/Why3Translator.cpp12
-rw-r--r--libsolidity/parsing/Token.cpp3
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp23
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp437
-rw-r--r--test/libsolidity/SolidityParser.cpp46
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()
}