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)
|
void ConstantEvaluator::endVisit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
|
// TODO handle user suffix
|
||||||
if (Type const* literalType = TypeProvider::forLiteral(_literal))
|
if (Type const* literalType = TypeProvider::forLiteral(_literal))
|
||||||
m_values[&_literal] = constantToTypedValue(*literalType);
|
m_values[&_literal] = constantToTypedValue(*literalType);
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include <range/v3/view/transform.hpp>
|
#include <range/v3/view/transform.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace solidity::util;
|
||||||
using namespace solidity::langutil;
|
using namespace solidity::langutil;
|
||||||
using namespace solidity::frontend;
|
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))
|
else if (optional<ConstantEvaluator::TypedRational> value = ConstantEvaluator::evaluate(m_errorReporter, *length))
|
||||||
lengthValue = value->value;
|
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(
|
m_errorReporter.typeError(
|
||||||
5462_error,
|
5462_error,
|
||||||
length->location(),
|
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)
|
else if (*lengthValue == 0)
|
||||||
m_errorReporter.typeError(1406_error, length->location(), "Array with zero length specified.");
|
m_errorReporter.typeError(1406_error, length->location(), "Array with zero length specified.");
|
||||||
|
@ -141,6 +141,8 @@ bool FunctionCallGraphBuilder::visit(EmitStatement const& _emitStatement)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO why don't we handle IdentifierPath here?
|
||||||
|
|
||||||
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
|
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
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/drop_exactly.hpp>
|
||||||
#include <range/v3/view/enumerate.hpp>
|
#include <range/v3/view/enumerate.hpp>
|
||||||
#include <range/v3/view/zip.hpp>
|
#include <range/v3/view/zip.hpp>
|
||||||
|
#include <range/v3/algorithm/any_of.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -3707,6 +3708,10 @@ bool TypeChecker::visit(Identifier const& _identifier)
|
|||||||
{
|
{
|
||||||
IdentifierAnnotation& annotation = _identifier.annotation();
|
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)
|
if (!annotation.referencedDeclaration)
|
||||||
{
|
{
|
||||||
annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations);
|
annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations);
|
||||||
@ -3737,9 +3742,32 @@ bool TypeChecker::visit(Identifier const& _identifier)
|
|||||||
|
|
||||||
for (Declaration const* declaration: annotation.overloadedDeclarations)
|
for (Declaration const* declaration: annotation.overloadedDeclarations)
|
||||||
{
|
{
|
||||||
FunctionTypePointer functionType = declaration->functionType(true);
|
FunctionTypePointer functionType = declaration->functionType(true /* _internal */);
|
||||||
solAssert(!!functionType, "Requested type not present.");
|
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);
|
candidates.push_back(declaration);
|
||||||
}
|
}
|
||||||
if (candidates.size() == 1)
|
if (candidates.size() == 1)
|
||||||
@ -3861,12 +3889,13 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
|
|||||||
_expr.annotation().isConstant = false;
|
_expr.annotation().isConstant = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::endVisit(Literal const& _literal)
|
bool TypeChecker::visit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
|
Type const* literalType = nullptr;
|
||||||
if (_literal.looksLikeAddress())
|
if (_literal.looksLikeAddress())
|
||||||
{
|
{
|
||||||
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses
|
// 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;
|
string msg;
|
||||||
if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits
|
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(
|
m_errorReporter.fatalTypeError(
|
||||||
5145_error,
|
5145_error,
|
||||||
_literal.location(),
|
_literal.location(),
|
||||||
@ -3900,22 +3929,132 @@ void TypeChecker::endVisit(Literal const& _literal)
|
|||||||
"You can use an expression of the form \"0x1234 * 1 day\" instead."
|
"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(
|
m_errorReporter.typeError(
|
||||||
4820_error,
|
4820_error,
|
||||||
_literal.location(),
|
_literal.location(),
|
||||||
"Using \"years\" as a unit denomination is deprecated."
|
"Using \"years\" as a unit denomination is deprecated."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_literal.annotation().type)
|
if (!literalType)
|
||||||
_literal.annotation().type = TypeProvider::forLiteral(_literal);
|
literalType = TypeProvider::forLiteral(_literal);
|
||||||
|
|
||||||
if (!_literal.annotation().type)
|
if (!literalType)
|
||||||
m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value.");
|
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().isLValue = false;
|
||||||
_literal.annotation().isConstant = 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)
|
void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||||
|
@ -164,6 +164,7 @@ private:
|
|||||||
void endVisit(IdentifierPath const& _identifierPath) override;
|
void endVisit(IdentifierPath const& _identifierPath) override;
|
||||||
void endVisit(UserDefinedTypeName const& _userDefinedTypeName) override;
|
void endVisit(UserDefinedTypeName const& _userDefinedTypeName) override;
|
||||||
void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
||||||
|
bool visit(Literal const& _literal) override;
|
||||||
void endVisit(Literal const& _literal) override;
|
void endVisit(Literal const& _literal) override;
|
||||||
void endVisit(UsingForDirective const& _usingForDirective) 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
|
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
||||||
/// not fit any type.
|
/// not fit any type.
|
||||||
|
/// Ignores user-defined suffixes.
|
||||||
static Type const* forLiteral(Literal const& _literal);
|
static Type const* forLiteral(Literal const& _literal);
|
||||||
static RationalNumberType const* rationalNumber(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));
|
return make_tuple(false, rational(0));
|
||||||
}
|
}
|
||||||
switch (_literal.subDenomination())
|
|
||||||
{
|
if (_literal.hasSubDenomination())
|
||||||
|
switch (_literal.subDenomination())
|
||||||
|
{
|
||||||
case Literal::SubDenomination::None:
|
case Literal::SubDenomination::None:
|
||||||
case Literal::SubDenomination::Wei:
|
case Literal::SubDenomination::Wei:
|
||||||
case Literal::SubDenomination::Second:
|
case Literal::SubDenomination::Second:
|
||||||
@ -995,8 +997,8 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
|||||||
case Literal::SubDenomination::Year:
|
case Literal::SubDenomination::Year:
|
||||||
value *= bigint("31536000");
|
value *= bigint("31536000");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// TODO: Do we need to consider literal suffixes here?
|
||||||
|
|
||||||
return make_tuple(true, value);
|
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):
|
StringLiteralType::StringLiteralType(Literal const& _literal):
|
||||||
m_value(_literal.value())
|
m_value(_literal.value())
|
||||||
{
|
{
|
||||||
|
@ -602,6 +602,14 @@ public:
|
|||||||
/// @returns true if the value is not an integer.
|
/// @returns true if the value is not an integer.
|
||||||
bool isFractional() const { return m_value.denominator() != 1; }
|
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.
|
/// @returns true if the value is negative.
|
||||||
bool isNegative() const { return m_value < 0; }
|
bool isNegative() const { return m_value < 0; }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user