Merge pull request #402 from VoR0220/fixedDataType

Fixed Type initial PR
This commit is contained in:
chriseth 2016-05-12 12:30:10 +02:00
commit 1ab0f25dff
13 changed files with 1056 additions and 219 deletions

View File

@ -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()))

View File

@ -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));
}

View File

@ -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)

View File

@ -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)
{
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);
}
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;
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::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
{
bigint x(_literal.value());
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 false;
return make_tuple(false, rational(0));
}
return true;
}
IntegerConstantType::IntegerConstantType(Literal const& _literal)
{
m_value = bigint(_literal.value());
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;
case Token::Exp:
if (other.m_value < 0)
return TypePointer();
else if (other.m_value > numeric_limits<unsigned int>::max())
return TypePointer();
else if (fractional)
{
rational tempValue = m_value / other.m_value;
value = m_value - (tempValue.numerator() / tempValue.denominator()) * other.m_value;
}
else
value = boost::multiprecision::pow(m_value, other.m_value.convert_to<unsigned int>());
value = m_value.numerator() % other.m_value.numerator();
break;
case Token::Exp:
{
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
// 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)

View File

@ -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; }
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 RationalNumberType: public Type
{
public:
virtual Category category() const override { return Category::RationalNumber; }
/// @returns true if the literal is a valid integer.
static bool isValidLiteral(Literal const& _literal);
explicit IntegerConstantType(Literal const& _literal);
explicit IntegerConstantType(bigint _value): m_value(_value) {}
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;
};
/**

View File

@ -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);
}

View File

@ -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:

View File

@ -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;

View File

@ -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.");
}

View File

@ -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)

View File

@ -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)

View File

@ -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,19 +3216,6 @@ BOOST_AUTO_TEST_CASE(int10abc_is_identifier)
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(invalid_fixed_types)
{
char const* text = R"(
contract test {
function f() {
fixed0x7 a = .3;
fixed99999999999999999999999999999999999999x7 b = 9.5;
}
}
)";
BOOST_CHECK(!success(text));
}
BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value)
{
char const* text = R"(
@ -3256,6 +3229,36 @@ BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value)
BOOST_CHECK(!success(text));
}
BOOST_AUTO_TEST_CASE(invalid_fixed_types_0x7_mxn)
{
char const* text = R"(
contract test {
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));
}
BOOST_AUTO_TEST_CASE(library_instances_cannot_be_used)
{
char const* text = R"(
@ -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()
}

View File

@ -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()
}