mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[TMP] Replace IdentifierPath with Identifier/Member access for literal suffixes
This commit is contained in:
parent
a5a294053d
commit
d98b12420b
@ -234,10 +234,8 @@ bool FunctionCallGraphBuilder::visit(NewExpression const& _newExpression)
|
||||
|
||||
bool FunctionCallGraphBuilder::visit(Literal const& _literal)
|
||||
{
|
||||
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
functionReferenced(
|
||||
dynamic_cast<FunctionDefinition const&>(*(*identifierPath)->annotation().referencedDeclaration)
|
||||
);
|
||||
if (_literal.suffixFunction())
|
||||
functionReferenced(*_literal.suffixFunction());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3709,6 +3709,7 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
}
|
||||
|
||||
Literal::SubDenomination const* subDenomination = get_if<Literal::SubDenomination>(&_literal.suffix());
|
||||
|
||||
if (_literal.isHexNumber() && subDenomination && *subDenomination != Literal::SubDenomination::None)
|
||||
m_errorReporter.fatalTypeError(
|
||||
5145_error,
|
||||
@ -3733,14 +3734,21 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
// TODO at this point 'type' needs to be stored for code generation.
|
||||
|
||||
bool isCompileTimeConstant = true;
|
||||
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
if (!subDenomination)
|
||||
{
|
||||
Declaration const* declaration = (*identifierPath)->annotation().referencedDeclaration;
|
||||
FunctionDefinition const* definition = dynamic_cast<FunctionDefinition const*>(declaration);
|
||||
FunctionType const* suffixFunctionType = dynamic_cast<FunctionType const*>(std::visit(GenericVisitor{
|
||||
[&](ASTPointer<Identifier> const& _identifier) { return _identifier->annotation().type; },
|
||||
[&](ASTPointer<MemberAccess> const& _memberAccess) { return _memberAccess->annotation().type; },
|
||||
[&](Literal::SubDenomination) -> Type const* { solAssert(false); },
|
||||
}, _literal.suffix()));
|
||||
|
||||
if (
|
||||
!definition ||
|
||||
!definition->isFree() ||
|
||||
definition->stateMutability() != StateMutability::Pure
|
||||
!suffixFunctionType || // Rejects variables
|
||||
!suffixFunctionType->hasDeclaration() || // Rejects function pointers
|
||||
!dynamic_cast<FunctionDefinition const*>(&suffixFunctionType->declaration()) || // Rejects events and errors
|
||||
suffixFunctionType->bound() ||
|
||||
!_literal.suffixFunction()->isFree() ||
|
||||
_literal.suffixFunction()->stateMutability() != StateMutability::Pure
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
4438_error,
|
||||
@ -3749,24 +3757,23 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
);
|
||||
else
|
||||
{
|
||||
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*declaration->type());
|
||||
solAssert(!functionType.takesArbitraryParameters());
|
||||
solAssert(functionType.kind() == FunctionType::Kind::Internal);
|
||||
solAssert(!suffixFunctionType->takesArbitraryParameters());
|
||||
solAssert(suffixFunctionType->kind() == FunctionType::Kind::Internal);
|
||||
|
||||
auto const* rationalType = dynamic_cast<RationalNumberType const*>(type);
|
||||
auto const* literalRationalType = dynamic_cast<RationalNumberType const*>(type);
|
||||
|
||||
optional<string> parameterCountMessage;
|
||||
if (functionType.parameterTypes().size() == 0)
|
||||
if (suffixFunctionType->parameterTypes().size() == 0)
|
||||
parameterCountMessage = "Functions that take no arguments cannot be used as literal suffixes.";
|
||||
else if (functionType.parameterTypes().size() >= 3)
|
||||
else if (suffixFunctionType->parameterTypes().size() >= 3)
|
||||
parameterCountMessage = "Functions that take 3 or more arguments cannot be used as literal suffixes.";
|
||||
else if (functionType.parameterTypes().size() == 2 && !rationalType)
|
||||
else if (suffixFunctionType->parameterTypes().size() == 2 && !literalRationalType)
|
||||
parameterCountMessage = "Functions that take 2 arguments can only be used as literal suffixes for rational numbers.";
|
||||
|
||||
optional<string> parameterTypeMessage;
|
||||
if (parameterCountMessage.has_value())
|
||||
m_errorReporter.typeError(4778_error, _literal.location(), parameterCountMessage.value());
|
||||
else if (functionType.parameterTypes().size() == 2)
|
||||
else if (suffixFunctionType->parameterTypes().size() == 2)
|
||||
{
|
||||
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
|
||||
solAssert((mantissa && exponent) || (!mantissa && !exponent));
|
||||
@ -3778,25 +3785,27 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
"that fit the range of parameters of the suffix function."
|
||||
);
|
||||
else if (
|
||||
!mantissa->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(0)) ||
|
||||
!exponent->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(1))
|
||||
!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 (!type->isImplicitlyConvertibleTo(*functionType.parameterTypes().front()))
|
||||
else if (!type->isImplicitlyConvertibleTo(*suffixFunctionType->parameterTypes().front()))
|
||||
parameterTypeMessage = "The type of the literal cannot be converted to the parameter of the suffix function.";
|
||||
|
||||
if (parameterTypeMessage.has_value())
|
||||
m_errorReporter.typeError(8838_error, _literal.location(), parameterTypeMessage.value());
|
||||
|
||||
isCompileTimeConstant = functionType.isPure();
|
||||
if (functionType.returnParameterTypes().size() == 1)
|
||||
type = functionType.returnParameterTypes().front();
|
||||
isCompileTimeConstant = suffixFunctionType->isPure();
|
||||
if (suffixFunctionType->returnParameterTypes().size() == 1)
|
||||
type = suffixFunctionType->returnParameterTypes().front();
|
||||
else
|
||||
type = TypeProvider::tuple(functionType.returnParameterTypes());
|
||||
type = TypeProvider::tuple(suffixFunctionType->returnParameterTypes());
|
||||
}
|
||||
}
|
||||
else
|
||||
solAssert(holds_alternative<Literal::SubDenomination>(_literal.suffix()));
|
||||
|
||||
_literal.annotation().type = type;
|
||||
_literal.annotation().isPure = isCompileTimeConstant;
|
||||
|
@ -27,7 +27,9 @@
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/ast/AST_accept.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <libsolutil/Keccak256.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@ -940,6 +942,23 @@ ASTString Literal::valueWithoutUnderscores() const
|
||||
return boost::erase_all_copy(value(), "_");
|
||||
}
|
||||
|
||||
FunctionDefinition const* Literal::suffixFunction() const
|
||||
{
|
||||
if (holds_alternative<SubDenomination>(m_suffix))
|
||||
return nullptr;
|
||||
|
||||
Declaration const* referencedDeclaration = visit(util::GenericVisitor{
|
||||
[&](ASTPointer<Identifier> const& _identifier) { return _identifier->annotation().referencedDeclaration; },
|
||||
[&](ASTPointer<MemberAccess> const& _memberAccess) { return _memberAccess->annotation().referencedDeclaration; },
|
||||
[&](SubDenomination) -> Declaration const* { solAssert(false); },
|
||||
}, m_suffix);
|
||||
|
||||
solAssert(referencedDeclaration, "Literal suffix must have a definition.");
|
||||
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(referencedDeclaration);
|
||||
solAssert(functionDefinition, "Non-denomination literal suffix must be a function.");
|
||||
return functionDefinition;
|
||||
}
|
||||
|
||||
bool Literal::isHexNumber() const
|
||||
{
|
||||
if (token() != Token::Number)
|
||||
|
@ -2351,7 +2351,7 @@ public:
|
||||
Week = static_cast<int>(Token::SubWeek),
|
||||
Year = static_cast<int>(Token::SubYear)
|
||||
};
|
||||
using Suffix = std::variant<SubDenomination, ASTPointer<IdentifierPath>>;
|
||||
using Suffix = std::variant<SubDenomination, ASTPointer<Identifier>, ASTPointer<MemberAccess>>;
|
||||
Literal(
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
@ -2371,6 +2371,9 @@ public:
|
||||
|
||||
//SubDenomination subDenomination() const { return m_suffix; }
|
||||
Suffix const& suffix() const { return m_suffix; }
|
||||
/// @returns Function definition associated with the suffix if the suffix is not a subdenomination.
|
||||
/// nullptr otherwise.
|
||||
FunctionDefinition const* suffixFunction() const;
|
||||
|
||||
/// @returns true if this is a number with a hex prefix.
|
||||
bool isHexNumber() const;
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
@ -1015,16 +1017,23 @@ void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
|
||||
void Literal::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
|
||||
(*identifierPath)->accept(_visitor);
|
||||
std::visit(solidity::util::GenericVisitor{
|
||||
[&](ASTPointer<Identifier> const& _identifier) { _identifier->accept(_visitor); },
|
||||
[&](ASTPointer<MemberAccess> const& _memberAccess) { _memberAccess->accept(_visitor); },
|
||||
[&](SubDenomination) {},
|
||||
}, m_suffix);
|
||||
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void Literal::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
|
||||
(*identifierPath)->accept(_visitor);
|
||||
std::visit(solidity::util::GenericVisitor{
|
||||
[&](ASTPointer<Identifier> const& _identifier) { _identifier->accept(_visitor); },
|
||||
[&](ASTPointer<MemberAccess> const& _memberAccess) { _memberAccess->accept(_visitor); },
|
||||
[&](SubDenomination) {},
|
||||
}, m_suffix);
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
|
@ -2271,12 +2271,9 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
||||
|
||||
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
if (_literal.suffixDefinition())
|
||||
{
|
||||
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
|
||||
*(*identifierPath)->annotation().referencedDeclaration
|
||||
);
|
||||
FunctionType const& functionType = *function.functionType(true);
|
||||
FunctionType const& functionType = *_literal.suffixDefinition()->functionType(true);
|
||||
|
||||
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
|
||||
|
||||
@ -2298,7 +2295,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
|
||||
m_context << type->literalValue(&_literal);
|
||||
utils().convertType(*type, *functionType.parameterTypes().at(0));
|
||||
}
|
||||
m_context << m_context.functionEntryLabel(function).pushTag();
|
||||
m_context << m_context.functionEntryLabel(*_literal.suffixDefinition()).pushTag();
|
||||
m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
|
||||
m_context << returnLabel;
|
||||
|
||||
@ -2311,6 +2308,8 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(holds_alternative<Literal::SubDenomination>(_literal.suffix()));
|
||||
|
||||
Type const* type = _literal.annotation().type;
|
||||
|
||||
|
||||
|
@ -2438,12 +2438,9 @@ bool IRGeneratorForStatements::visit(Literal const& _literal)
|
||||
{
|
||||
setLocation(_literal);
|
||||
|
||||
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
if (_literal.suffixFunction())
|
||||
{
|
||||
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
|
||||
*(*identifierPath)->annotation().referencedDeclaration
|
||||
);
|
||||
FunctionType const& functionType = *function.functionType(true);
|
||||
FunctionType const& functionType = *_literal.suffixFunction()->functionType(true);
|
||||
|
||||
// TODO this is actually not always the right one.
|
||||
auto type = TypeProvider::forLiteral(_literal);
|
||||
@ -2472,11 +2469,12 @@ bool IRGeneratorForStatements::visit(Literal const& _literal)
|
||||
// TODO what about string?
|
||||
}
|
||||
define(_literal) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(function) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(*_literal.suffixFunction()) <<
|
||||
"(" + joinHumanReadable(args) + ")\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(holds_alternative<Literal::SubDenomination>(_literal.suffix()));
|
||||
Type const& literalType = type(_literal);
|
||||
|
||||
switch (literalType.category())
|
||||
|
@ -35,10 +35,12 @@
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
@ -1996,9 +1998,31 @@ ASTPointer<Expression> Parser::parseLiteral()
|
||||
}
|
||||
else if (m_scanner->currentToken() == Token::Identifier)
|
||||
{
|
||||
auto identifierPath = parseIdentifierPath();
|
||||
nodeFactory.setEndPositionFromNode(identifierPath);
|
||||
suffix = move(identifierPath);
|
||||
// TODO: Make sure locations are set correctly
|
||||
ASTPointer<ASTString> suffixName = make_shared<ASTString>(m_scanner->currentLiteral());
|
||||
nodeFactory.markEndPosition();
|
||||
ASTPointer<Identifier> identifier = nodeFactory.createNode<Identifier>(suffixName);
|
||||
|
||||
advance();
|
||||
|
||||
if (m_scanner->currentToken() != Token::Period)
|
||||
suffix = identifier;
|
||||
else
|
||||
{
|
||||
ASTPointer<Expression> memberAccess = identifier;
|
||||
do
|
||||
{
|
||||
// FIXME: This grabs the semicolon
|
||||
advance();
|
||||
nodeFactory.markEndPosition();
|
||||
SourceLocation memberLocation = currentLocation();
|
||||
ASTPointer<ASTString> memberName = expectIdentifierToken();
|
||||
memberAccess = nodeFactory.createNode<MemberAccess>(memberAccess, move(memberName), move(memberLocation));
|
||||
}
|
||||
while (m_scanner->currentToken() == Token::Period);
|
||||
|
||||
suffix = dynamic_pointer_cast<MemberAccess>(memberAccess);
|
||||
}
|
||||
}
|
||||
return nodeFactory.createNode<Literal>(initialToken, move(value), move(suffix));
|
||||
}
|
||||
|
@ -10,4 +10,4 @@ contract C {
|
||||
int d = "a" suffix; // TODO: Should match only string
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (216-222): Identifier not found or not unique.
|
||||
// TypeError 2144: (214-222): No matching declaration found after variable lookup.
|
||||
|
@ -18,4 +18,4 @@ contract C {
|
||||
int f = -2.55 iuSuffix; // TODO: Should match only (uint8, uint)
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (347-354): Identifier not found or not unique.
|
||||
// TypeError 2144: (341-354): No matching declaration found after variable lookup.
|
||||
|
@ -2,7 +2,7 @@ function iuSuffix(uint8, uint) pure returns (uint) {}
|
||||
function iuSuffix(int8, uint) pure returns (uint) {}
|
||||
|
||||
contract C {
|
||||
uint a = 1.27 iuSuffix;
|
||||
uint a = 1.27 iuSuffix; // TODO: Error should say it's ambiguous
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (139-147): Identifier not found or not unique.
|
||||
// TypeError 2144: (134-147): No matching declaration found after variable lookup.
|
||||
|
@ -2,7 +2,7 @@ function uSuffix(uint8, uint) pure returns (uint) {}
|
||||
function uSuffix(uint16, uint) pure returns (uint) {}
|
||||
|
||||
contract C {
|
||||
uint a = 1.27 uSuffix;
|
||||
uint a = 1.27 uSuffix; // TODO: Error should say it's ambiguous
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (139-146): Identifier not found or not unique.
|
||||
// TypeError 2144: (134-146): No matching declaration found after variable lookup.
|
||||
|
@ -11,4 +11,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (263-272): Identifier not found or not unique.
|
||||
// TypeError 2144: (259-272): No matching declaration found after variable lookup.
|
||||
|
@ -3,8 +3,8 @@ function suffix(uint, uint) pure returns (int) {}
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
int a = 1 suffix;
|
||||
int a = 1 suffix; // TODO: Error should say it's ambiguous
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (157-163): Identifier not found or not unique.
|
||||
// TypeError 2144: (155-163): No matching declaration found after variable lookup.
|
||||
|
@ -18,4 +18,4 @@ contract C {
|
||||
int f = -255 iuSuffix; // TODO: Should match only uint8
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (310-317): Identifier not found or not unique.
|
||||
// TypeError 2144: (305-317): No matching declaration found after variable lookup.
|
||||
|
@ -2,7 +2,7 @@ function iuSuffix(uint8) pure returns (int) {}
|
||||
function iuSuffix(int8) pure returns (int) {}
|
||||
|
||||
contract C {
|
||||
int a = 127 iuSuffix;
|
||||
int a = 127 iuSuffix; // TODO: Error should say it's ambiguous
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (123-131): Identifier not found or not unique.
|
||||
// TypeError 2144: (119-131): No matching declaration found after variable lookup.
|
||||
|
@ -2,7 +2,7 @@ function uSuffix(uint8) pure returns (int) {}
|
||||
function uSuffix(uint16) pure returns (int) {}
|
||||
|
||||
contract C {
|
||||
int a = 127 uSuffix;
|
||||
int a = 127 uSuffix; // TODO: Error should say it's ambiguous
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (123-130): Identifier not found or not unique.
|
||||
// TypeError 2144: (119-130): No matching declaration found after variable lookup.
|
||||
|
@ -5,4 +5,4 @@ contract C {
|
||||
int a = hex"abcd" suffix; // TODO: Should match only bytes
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (141-147): Identifier not found or not unique.
|
||||
// TypeError 2144: (131-147): No matching declaration found after variable lookup.
|
||||
|
@ -2,7 +2,7 @@ function suffix(string memory) pure returns (uint) {}
|
||||
function suffix(bytes memory) pure returns (uint) {}
|
||||
|
||||
contract C {
|
||||
uint a = "abcd" suffix;
|
||||
uint a = "abcd" suffix; // TODO: Error should say it's ambiguous
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (141-147): Identifier not found or not unique.
|
||||
// TypeError 2144: (134-147): No matching declaration found after variable lookup.
|
||||
|
@ -7,4 +7,4 @@ contract C {
|
||||
uint b = 1000 a.suffix;
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (149-157): Identifier not found or not unique.
|
||||
// TypeError 4438: (144-157): The literal suffix needs to be a pre-defined suffix or a file-level pure function.
|
||||
|
@ -9,4 +9,4 @@ contract C {
|
||||
uint b = 1000 a.suffix;
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (169-177): Identifier not found or not unique.
|
||||
// TypeError 4438: (164-177): The literal suffix needs to be a pre-defined suffix or a file-level pure function.
|
||||
|
@ -4,4 +4,4 @@ contract C {
|
||||
function suffix(uint x) external pure returns (uint) { return x; }
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (31-42): Identifier not found or not unique.
|
||||
// TypeError 4438: (26-42): The literal suffix needs to be a pre-defined suffix or a file-level pure function.
|
||||
|
@ -6,4 +6,4 @@ contract C {
|
||||
uint x = 1000 L.suffix;
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (117-125): Identifier not found or not unique.
|
||||
// TypeError 4438: (112-125): The literal suffix needs to be a pre-defined suffix or a file-level pure function.
|
||||
|
@ -4,4 +4,4 @@ contract C {
|
||||
uint a = 1 this.g;
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (48-54): Identifier not found or not unique.
|
||||
// TypeError 4438: (46-54): The literal suffix needs to be a pre-defined suffix or a file-level pure function.
|
||||
|
@ -4,4 +4,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (54-60): Identifier not found or not unique.
|
||||
// TypeError 2144: (52-60): No matching declaration found after variable lookup.
|
||||
|
@ -8,4 +8,4 @@ contract C is B {
|
||||
function suffix(uint x) internal pure override returns (uint) { return x; }
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 7920: (127-133): Identifier not found or not unique.
|
||||
// TypeError 4438: (122-133): The literal suffix needs to be a pre-defined suffix or a file-level pure function.
|
||||
|
Loading…
Reference in New Issue
Block a user