mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
User-defined literal suffixes: Analysis
This commit is contained in:
parent
fda86c21f8
commit
143722c705
@ -378,6 +378,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
||||
|
||||
void ConstantEvaluator::endVisit(Literal const& _literal)
|
||||
{
|
||||
// TODO handle user suffix
|
||||
if (Type const* literalType = TypeProvider::forLiteral(_literal))
|
||||
m_values[&_literal] = constantToTypedValue(*literalType);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <range/v3/view/transform.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
@ -342,11 +343,16 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
|
||||
else if (optional<ConstantEvaluator::TypedRational> value = ConstantEvaluator::evaluate(m_errorReporter, *length))
|
||||
lengthValue = value->value;
|
||||
|
||||
if (!lengthValue)
|
||||
string suffixErrorMessage;
|
||||
if (auto const* lengthLiteral = dynamic_cast<Literal const*>(length))
|
||||
if (lengthLiteral->isSuffixed() && !lengthLiteral->hasSubDenomination())
|
||||
suffixErrorMessage = " A suffixed literal is not a constant expression unless the suffix is a denomination.";
|
||||
|
||||
if (!lengthValue || !suffixErrorMessage.empty())
|
||||
m_errorReporter.typeError(
|
||||
5462_error,
|
||||
length->location(),
|
||||
"Invalid array length, expected integer literal or constant expression."
|
||||
"Invalid array length, expected integer literal or constant expression." + suffixErrorMessage
|
||||
);
|
||||
else if (*lengthValue == 0)
|
||||
m_errorReporter.typeError(1406_error, length->location(), "Array with zero length specified.");
|
||||
|
@ -141,6 +141,8 @@ bool FunctionCallGraphBuilder::visit(EmitStatement const& _emitStatement)
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO why don't we handle IdentifierPath here?
|
||||
|
||||
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
|
||||
{
|
||||
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <range/v3/view/drop_exactly.hpp>
|
||||
#include <range/v3/view/enumerate.hpp>
|
||||
#include <range/v3/view/zip.hpp>
|
||||
#include <range/v3/algorithm/any_of.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -3707,6 +3708,10 @@ bool TypeChecker::visit(Identifier const& _identifier)
|
||||
{
|
||||
IdentifierAnnotation& annotation = _identifier.annotation();
|
||||
|
||||
// NOTE: referencedDeclaration is already be set at this point if the reference resolver found
|
||||
// exactly one candidate. We do not put this candidate through the filters below so make sure
|
||||
// other parts of type checker actually validate these things too.
|
||||
// E.g. it might be a non-suffix function used as a suffix.
|
||||
if (!annotation.referencedDeclaration)
|
||||
{
|
||||
annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations);
|
||||
@ -3737,9 +3742,32 @@ bool TypeChecker::visit(Identifier const& _identifier)
|
||||
|
||||
for (Declaration const* declaration: annotation.overloadedDeclarations)
|
||||
{
|
||||
FunctionTypePointer functionType = declaration->functionType(true);
|
||||
FunctionTypePointer functionType = declaration->functionType(true /* _internal */);
|
||||
solAssert(!!functionType, "Requested type not present.");
|
||||
if (functionType->canTakeArguments(*annotation.arguments))
|
||||
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(declaration);
|
||||
|
||||
bool argumentsMatch = false;
|
||||
if (_identifier.annotation().suffixedLiteral && !(functionDefinition && functionDefinition->usableAsSuffix()))
|
||||
argumentsMatch = false;
|
||||
else if (functionType->canTakeArguments(*annotation.arguments))
|
||||
argumentsMatch = true;
|
||||
else if (_identifier.annotation().suffixedLiteral && functionType->parameterTypes().size() == 2)
|
||||
{
|
||||
Type const* literalType = _identifier.annotation().suffixedLiteral->annotation().type;
|
||||
auto const* literalRationalType = dynamic_cast<RationalNumberType const*>(literalType);
|
||||
if (literalRationalType)
|
||||
{
|
||||
auto&& [mantissa, exponent] = literalRationalType->mantissaExponent();
|
||||
// This was already validated in visit(Literal) but the error is not fatal.
|
||||
if (!mantissa || !exponent)
|
||||
solAssert(!m_errorReporter.errors().empty());
|
||||
else
|
||||
argumentsMatch = functionType->canTakeArguments({{mantissa, exponent}, {}});
|
||||
}
|
||||
}
|
||||
|
||||
if (argumentsMatch)
|
||||
candidates.push_back(declaration);
|
||||
}
|
||||
if (candidates.size() == 1)
|
||||
@ -3861,12 +3889,13 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
|
||||
_expr.annotation().isConstant = false;
|
||||
}
|
||||
|
||||
void TypeChecker::endVisit(Literal const& _literal)
|
||||
bool TypeChecker::visit(Literal const& _literal)
|
||||
{
|
||||
Type const* literalType = nullptr;
|
||||
if (_literal.looksLikeAddress())
|
||||
{
|
||||
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses
|
||||
_literal.annotation().type = TypeProvider::address();
|
||||
literalType = TypeProvider::address();
|
||||
|
||||
string msg;
|
||||
if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits
|
||||
@ -3892,7 +3921,7 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
);
|
||||
}
|
||||
|
||||
if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None)
|
||||
if (_literal.isHexNumber() && _literal.hasSubDenomination())
|
||||
m_errorReporter.fatalTypeError(
|
||||
5145_error,
|
||||
_literal.location(),
|
||||
@ -3900,22 +3929,132 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
"You can use an expression of the form \"0x1234 * 1 day\" instead."
|
||||
);
|
||||
|
||||
if (_literal.subDenomination() == Literal::SubDenomination::Year)
|
||||
if (_literal.hasSubDenomination() && _literal.subDenomination() == Literal::SubDenomination::Year)
|
||||
m_errorReporter.typeError(
|
||||
4820_error,
|
||||
_literal.location(),
|
||||
"Using \"years\" as a unit denomination is deprecated."
|
||||
);
|
||||
|
||||
if (!_literal.annotation().type)
|
||||
_literal.annotation().type = TypeProvider::forLiteral(_literal);
|
||||
if (!literalType)
|
||||
literalType = TypeProvider::forLiteral(_literal);
|
||||
|
||||
if (!_literal.annotation().type)
|
||||
if (!literalType)
|
||||
m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value.");
|
||||
|
||||
_literal.annotation().isPure = true;
|
||||
std::visit(GenericVisitor{
|
||||
[&](ASTPointer<Identifier> const& _identifier) {
|
||||
_identifier->annotation().suffixedLiteral = &_literal;
|
||||
_identifier->annotation().arguments = {{literalType}, {}};
|
||||
_identifier->annotation().calledDirectly = true;
|
||||
},
|
||||
[&](ASTPointer<MemberAccess> const& _memberAccess) {
|
||||
_memberAccess->annotation().suffixedLiteral = &_literal;
|
||||
_memberAccess->annotation().arguments = {{literalType}, {}};
|
||||
_memberAccess->annotation().calledDirectly = true;
|
||||
},
|
||||
[&](Literal::SubDenomination) {},
|
||||
}, _literal.suffix());
|
||||
|
||||
auto const* literalRationalType = dynamic_cast<RationalNumberType const*>(literalType);
|
||||
if (_literal.isSuffixed() && !_literal.hasSubDenomination() && literalRationalType)
|
||||
{
|
||||
auto&& [mantissa, exponent] = literalRationalType->mantissaExponent();
|
||||
solAssert((mantissa && exponent) || (!mantissa && !exponent));
|
||||
if (!mantissa)
|
||||
m_errorReporter.typeError(
|
||||
5503_error,
|
||||
_literal.location(),
|
||||
"This fractional number cannot be decomposed into a mantissa and decimal exponent "
|
||||
"that fit the range of parameters of any possible suffix function."
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: For suffixed literals this is not the final type yet. We will update it in endVisit()
|
||||
// when we know what the suffix function returns.
|
||||
_literal.annotation().type = literalType;
|
||||
_literal.annotation().isLValue = false;
|
||||
_literal.annotation().isConstant = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TypeChecker::endVisit(Literal const& _literal)
|
||||
{
|
||||
bool isCompileTimeConstant = true;
|
||||
if (_literal.isSuffixed() && !_literal.hasSubDenomination())
|
||||
{
|
||||
FunctionType const* suffixFunctionType = _literal.suffixFunctionType();
|
||||
|
||||
if (
|
||||
!suffixFunctionType || // Rejects variables
|
||||
!suffixFunctionType->hasDeclaration() || // Rejects function pointers
|
||||
!dynamic_cast<FunctionDefinition const*>(&suffixFunctionType->declaration()) || // Rejects events and errors
|
||||
suffixFunctionType->hasBoundFirstArgument() ||
|
||||
!_literal.suffixFunction()->usableAsSuffix()
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
4438_error,
|
||||
_literal.location(),
|
||||
"The literal suffix must be either a subdenomination or a file-level suffix function."
|
||||
);
|
||||
else
|
||||
{
|
||||
solAssert(_literal.suffixFunction());
|
||||
solAssert(!_literal.suffixFunction()->virtualSemantics());
|
||||
solAssert(!suffixFunctionType->takesArbitraryParameters());
|
||||
solAssert(suffixFunctionType->kind() == FunctionType::Kind::Internal);
|
||||
|
||||
Type const* literalType = _literal.annotation().type;
|
||||
auto const* literalRationalType = dynamic_cast<RationalNumberType const*>(literalType);
|
||||
|
||||
optional<string> parameterTypeMessage;
|
||||
if (suffixFunctionType->parameterTypes().size() == 2)
|
||||
{
|
||||
if (!literalRationalType)
|
||||
m_errorReporter.typeError(
|
||||
2505_error,
|
||||
_literal.location(),
|
||||
"Functions that take 2 arguments can only be used as literal suffixes for rational numbers."
|
||||
);
|
||||
else
|
||||
{
|
||||
auto&& [mantissa, exponent] = literalRationalType->mantissaExponent();
|
||||
// This was already validated in visit(Literal) but the error is not fatal.
|
||||
if (!mantissa || !exponent)
|
||||
solAssert(!m_errorReporter.errors().empty());
|
||||
else if (
|
||||
!mantissa->isImplicitlyConvertibleTo(*suffixFunctionType->parameterTypes().at(0)) ||
|
||||
!exponent->isImplicitlyConvertibleTo(*suffixFunctionType->parameterTypes().at(1))
|
||||
)
|
||||
// TODO: Is this triggered when the argument is out of range? Test.
|
||||
parameterTypeMessage = "The type of the literal cannot be converted to the parameters of the suffix function.";
|
||||
}
|
||||
}
|
||||
else if (suffixFunctionType->parameterTypes().size() == 1)
|
||||
{
|
||||
if (!literalType->isImplicitlyConvertibleTo(*suffixFunctionType->parameterTypes().at(0)))
|
||||
parameterTypeMessage = "The type of the literal cannot be converted to the parameter of the suffix function.";
|
||||
}
|
||||
else
|
||||
solAssert(m_errorReporter.hasErrors());
|
||||
|
||||
if (parameterTypeMessage.has_value())
|
||||
m_errorReporter.typeError(8838_error, _literal.location(), parameterTypeMessage.value());
|
||||
|
||||
if (suffixFunctionType->returnParameterTypes().size() == 1)
|
||||
_literal.annotation().type = suffixFunctionType->returnParameterTypes().front();
|
||||
else
|
||||
{
|
||||
solAssert(m_errorReporter.hasErrors());
|
||||
_literal.annotation().type = TypeProvider::tuple(suffixFunctionType->returnParameterTypes());
|
||||
}
|
||||
|
||||
isCompileTimeConstant = suffixFunctionType->isPure();
|
||||
}
|
||||
}
|
||||
|
||||
_literal.annotation().isPure = isCompileTimeConstant;
|
||||
}
|
||||
|
||||
void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
|
@ -164,6 +164,7 @@ private:
|
||||
void endVisit(IdentifierPath const& _identifierPath) override;
|
||||
void endVisit(UserDefinedTypeName const& _userDefinedTypeName) override;
|
||||
void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
||||
bool visit(Literal const& _literal) override;
|
||||
void endVisit(Literal const& _literal) override;
|
||||
void endVisit(UsingForDirective const& _usingForDirective) override;
|
||||
|
||||
|
@ -167,6 +167,7 @@ public:
|
||||
|
||||
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
||||
/// not fit any type.
|
||||
/// Ignores user-defined suffixes.
|
||||
static Type const* forLiteral(Literal const& _literal);
|
||||
static RationalNumberType const* rationalNumber(Literal const& _literal);
|
||||
|
||||
|
@ -968,8 +968,10 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
||||
{
|
||||
return make_tuple(false, rational(0));
|
||||
}
|
||||
switch (_literal.subDenomination())
|
||||
{
|
||||
|
||||
if (_literal.hasSubDenomination())
|
||||
switch (_literal.subDenomination())
|
||||
{
|
||||
case Literal::SubDenomination::None:
|
||||
case Literal::SubDenomination::Wei:
|
||||
case Literal::SubDenomination::Second:
|
||||
@ -995,8 +997,8 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
||||
case Literal::SubDenomination::Year:
|
||||
value *= bigint("31536000");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// TODO: Do we need to consider literal suffixes here?
|
||||
|
||||
return make_tuple(true, value);
|
||||
}
|
||||
@ -1263,6 +1265,36 @@ FixedPointType const* RationalNumberType::fixedPointType() const
|
||||
);
|
||||
}
|
||||
|
||||
pair<RationalNumberType const*, RationalNumberType const*> RationalNumberType::mantissaExponent() const
|
||||
{
|
||||
rational const maxUint = rational((bigint(1) << 256) - 1);
|
||||
rational const minInt = -rational(bigint(1) << 255);
|
||||
|
||||
bool negative = (m_value < 0);
|
||||
rational const maxMantissa = (negative ? -minInt : maxUint);
|
||||
|
||||
rational exponent = 0;
|
||||
rational unsignedMantissa = abs(m_value);
|
||||
|
||||
if (unsignedMantissa > maxMantissa)
|
||||
return {nullptr, nullptr};
|
||||
|
||||
while (unsignedMantissa.denominator() != 1)
|
||||
{
|
||||
unsignedMantissa *= 10;
|
||||
--exponent;
|
||||
|
||||
// FIXME: What happens when exponent in scientific notation is max uint?
|
||||
if (unsignedMantissa > maxMantissa || exponent > maxUint)
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
return {
|
||||
TypeProvider::rationalNumber(unsignedMantissa),
|
||||
TypeProvider::rationalNumber(-exponent),
|
||||
};
|
||||
}
|
||||
|
||||
StringLiteralType::StringLiteralType(Literal const& _literal):
|
||||
m_value(_literal.value())
|
||||
{
|
||||
|
@ -602,6 +602,14 @@ public:
|
||||
/// @returns true if the value is not an integer.
|
||||
bool isFractional() const { return m_value.denominator() != 1; }
|
||||
|
||||
// TODO: Update if it turns out we do need to support negative numbers here.
|
||||
/// Tries to decompose a positive rational number into two positive integers - a mantissa and a
|
||||
/// base-10 exponent, such that the number is equal to `mantissa * 10**-exponent`.
|
||||
/// @returns Pair of non-null pointers representing the types of the literals corresponding to
|
||||
/// mantissa and exponent if the resulting mantissa and exponent both fit in 256 bits.
|
||||
/// A pair of null pointers otherwise.
|
||||
std::pair<RationalNumberType const*, RationalNumberType const*> mantissaExponent() const;
|
||||
|
||||
/// @returns true if the value is negative.
|
||||
bool isNegative() const { return m_value < 0; }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user