mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #402 from VoR0220/fixedDataType
Fixed Type initial PR
This commit is contained in:
commit
1ab0f25dff
@ -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()))
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user