This commit is contained in:
Daniel Kirchner 2023-06-21 01:04:14 +02:00
parent 1c1110f734
commit 3286d1cec2
18 changed files with 307 additions and 32 deletions

View File

@ -273,6 +273,7 @@ namespace solidity::langutil
K(Class, "class", 0) \
K(Instantiation, "instantiation", 0) \
K(Word, "word", 0) \
K(Integer, "integer", 0) \
K(Void, "void", 0) \
K(StaticAssert, "static_assert", 0) \
T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \
@ -301,7 +302,7 @@ namespace TokenTraits
// Predicates
constexpr bool isElementaryTypeName(Token tok)
{
return (Token::Int <= tok && tok < Token::TypesEnd) || tok == Token::Word || tok == Token::Void;
return (Token::Int <= tok && tok < Token::TypesEnd) || tok == Token::Word || tok == Token::Void || tok == Token::Integer;
}
constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }

View File

@ -451,7 +451,9 @@ bool SyntaxChecker::visit(UsingForDirective const& _usingFor)
bool SyntaxChecker::visit(FunctionDefinition const& _function)
{
solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), "");
if (m_sourceUnit && m_sourceUnit->experimentalSolidity())
// Handled in experimental::SyntaxRestrictor instead.
return true;
if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified())
{
@ -506,3 +508,13 @@ bool SyntaxChecker::visit(StructDefinition const& _struct)
return true;
}
bool SyntaxChecker::visitNode(ASTNode const& _node)
{
if (_node.experimentalSolidityOnly())
{
solAssert(m_sourceUnit);
solAssert(m_sourceUnit->experimentalSolidity());
}
return ASTConstVisitor::visitNode(_node);
}

View File

@ -97,6 +97,8 @@ private:
bool visit(StructDefinition const& _struct) override;
bool visit(Literal const& _literal) override;
bool visitNode(ASTNode const&) override;
langutil::ErrorReporter& m_errorReporter;
bool m_useYulOptimizer = false;

View File

@ -32,7 +32,8 @@ bool SyntaxRestrictor::check(ASTNode const& _astRoot)
bool SyntaxRestrictor::visitNode(ASTNode const& _node)
{
m_errorReporter.syntaxError(0000_error, _node.location(), "Unsupported AST node.");
if (!_node.experimentalSolidityOnly())
m_errorReporter.syntaxError(0000_error, _node.location(), "Unsupported AST node.");
return false;
}

View File

@ -41,11 +41,13 @@ m_errorReporter(_analysis.errorReporter())
{BuiltinType::Function, "fun", 2},
{BuiltinType::Unit, "unit", 0},
{BuiltinType::Pair, "pair", 2},
{BuiltinType::Word, "word", 0}
{BuiltinType::Word, "word", 0},
{BuiltinType::Integer, "integer", 0}
})
m_typeSystem.declareBuiltinType(type, name, arity);
m_voidType = m_typeSystem.builtinType(BuiltinType::Void, {});
m_wordType = m_typeSystem.builtinType(BuiltinType::Word, {});
m_integerType = m_typeSystem.builtinType(BuiltinType::Integer, {});
m_typeAnnotations.resize(_analysis.maxAstId());
}
@ -84,7 +86,8 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
m_currentFunctionType = functionType;
_functionDefinition.body().accept(*this);
if (_functionDefinition.isImplemented())
_functionDefinition.body().accept(*this);
functionAnnotation.type = m_typeSystem.fresh(
functionType,
@ -143,13 +146,32 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
return m_wordType;
case Token::Void:
return m_voidType;
case Token::Integer:
return m_integerType;
default:
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
break;
}
}
else if (auto const* userDefinedTypeName = dynamic_cast<UserDefinedTypeName const*>(&_typeName))
{
auto const* declaration = userDefinedTypeName->pathNode().annotation().referencedDeclaration;
solAssert(declaration);
if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(declaration))
{
if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(variableDeclaration->scope()))
{
(void)typeClass;
m_errorReporter.typeError(0000_error, _typeName.location(), "Using type class type variables not yet implemented.");
}
else
m_errorReporter.typeError(0000_error, _typeName.location(), "Type name referencing a variable declaration.");
}
else
m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported user defined type name.");
}
else
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name.");
return m_typeSystem.freshTypeVariable(false);
}

View File

@ -57,6 +57,8 @@ private:
bool visit(Return const&) override { return true; }
void endVisit(Return const& _return) override;
bool visit(TypeClassDefinition const&) override { return true; }
bool visitNode(ASTNode const& _node) override;
Type fromTypeName(TypeName const& _typeName);
@ -65,6 +67,7 @@ private:
TypeSystem m_typeSystem;
Type m_voidType;
Type m_wordType;
Type m_integerType;
std::optional<Type> m_currentFunctionType;
struct TypeAnnotation

View File

@ -1058,3 +1058,11 @@ TryCatchClause const* TryStatement::errorClause() const {
TryCatchClause const* TryStatement::fallbackClause() const {
return findClause(m_clauses);
}
/// Experimental Solidity nodes
/// @{
TypeClassDefinitionAnnotation& TypeClassDefinition::annotation() const
{
return initAnnotation<TypeClassDefinitionAnnotation>();
}
/// @}

View File

@ -121,6 +121,8 @@ public:
bool operator!=(ASTNode const& _other) const { return !operator==(_other); }
///@}
virtual bool experimentalSolidityOnly() const { return false; }
protected:
size_t const m_id = 0;
@ -2447,4 +2449,43 @@ private:
/// @}
/// Experimental Solidity nodes
/// @{
class TypeClassDefinition: public Declaration, public StructurallyDocumented, public ScopeOpener
{
public:
TypeClassDefinition(
int64_t _id,
SourceLocation const& _location,
ASTPointer<VariableDeclaration> _typeVariable,
ASTPointer<ASTString> const& _name,
SourceLocation _nameLocation,
ASTPointer<StructuredDocumentation> const& _documentation,
std::vector<ASTPointer<ASTNode>> _subNodes
):
Declaration(_id, _location, _name, std::move(_nameLocation)),
StructurallyDocumented(_documentation),
m_typeVariable(std::move(_typeVariable)),
m_subNodes(std::move(_subNodes))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
VariableDeclaration const& typeVariable() const { return *m_typeVariable; }
std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; }
TypeClassDefinitionAnnotation& annotation() const override;
Type const* type() const override { solAssert(false, "Requested type of experimental solidity node."); }
bool experimentalSolidityOnly() const override { return true; }
private:
ASTPointer<VariableDeclaration> m_typeVariable;
std::vector<ASTPointer<ASTNode>> m_subNodes;
};
/// @}
}

View File

@ -341,4 +341,12 @@ struct FunctionCallAnnotation: ExpressionAnnotation
bool tryCall = false;
};
/// Experimental Solidity annotations.
/// Used to intergrate with name and type resolution.
/// @{
struct TypeClassDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
{
};
/// @}
}

View File

@ -1033,6 +1033,15 @@ void ASTJsonExporter::endVisit(EventDefinition const&)
m_inEvent = false;
}
bool ASTJsonExporter::visitNode(ASTNode const& _node)
{
solAssert(false, _node.experimentalSolidityOnly() ?
"Attempt to export an AST of experimental solidity." :
"Attempt to export an AST that contains unexpected nodes."
);
return false;
}
string ASTJsonExporter::location(VariableDeclaration::Location _location)
{
switch (_location)

View File

@ -130,6 +130,7 @@ public:
void endVisit(EventDefinition const&) override;
bool visitNode(ASTNode const& _node) override;
private:
void setJsonNode(
ASTNode const& _node,

View File

@ -109,6 +109,10 @@ public:
virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); }
virtual bool visit(Literal& _node) { return visitNode(_node); }
virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); }
/// @}
virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); }
virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); }
@ -165,6 +169,10 @@ public:
virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); }
virtual void endVisit(Literal& _node) { endVisitNode(_node); }
virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); }
/// @}
protected:
/// Generic function called by default for each node, to be overridden by derived classes
@ -243,6 +251,10 @@ public:
virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); }
virtual bool visit(Literal const& _node) { return visitNode(_node); }
virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); }
/// @}
virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); }
virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); }
@ -299,6 +311,10 @@ public:
virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); }
/// Experimental Solidity nodes
/// @{
virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); }
/// @}
protected:
/// Generic function called by default for each node, to be overridden by derived classes

View File

@ -1024,4 +1024,31 @@ void Literal::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
/// Experimental Solidity nodes
/// @{
void TypeClassDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_typeVariable->accept(_visitor);
if (m_documentation)
m_documentation->accept(_visitor);
listAccept(m_subNodes, _visitor);
}
_visitor.endVisit(*this);
}
void TypeClassDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_typeVariable->accept(_visitor);
if (m_documentation)
m_documentation->accept(_visitor);
listAccept(m_subNodes, _visitor);
}
_visitor.endVisit(*this);
}
/// @}
}

View File

@ -40,26 +40,52 @@ std::string TypeSystem::typeToString(Type const& _type) const
return std::visit(util::GenericVisitor{
[&](TypeExpression const& _type) {
std::stringstream stream;
if (!_type.arguments.empty())
{
stream << "(";
for (auto type: _type.arguments | ranges::views::drop_last(1))
stream << typeToString(type) << ", ";
stream << typeToString(_type.arguments.back());
stream << ") ";
}
stream << std::visit(util::GenericVisitor{
auto printTypeArguments = [&]() {
if (!_type.arguments.empty())
{
stream << "(";
for (auto type: _type.arguments | ranges::views::drop_last(1))
stream << typeToString(type) << ", ";
stream << typeToString(_type.arguments.back());
stream << ") ";
}
};
std::visit(util::GenericVisitor{
[&](Declaration const* _declaration) {
return _declaration->name();
printTypeArguments();
stream << _declaration->name();
},
[&](BuiltinType _builtinType) -> string {
return builtinTypeName(_builtinType);
[&](BuiltinType _builtinType) {
switch (_builtinType)
{
case BuiltinType::Function:
solAssert(_type.arguments.size() == 2);
stream << fmt::format("{} -> {}", typeToString(_type.arguments.front()), typeToString(_type.arguments.back()));
break;
case BuiltinType::Unit:
solAssert(_type.arguments.empty());
stream << "()";
break;
case BuiltinType::Pair:
{
auto tupleTypes = TypeSystemHelpers{*this}.destTupleType(_type);
stream << "(";
for (auto type: tupleTypes | ranges::view::drop_last(1))
stream << typeToString(type) << ", ";
stream << typeToString(tupleTypes.back()) << ")";
break;
}
default:
printTypeArguments();
stream << builtinTypeName(_builtinType);
break;
}
}
}, _type.constructor);
return stream.str();
},
[](TypeVariable const& _type) {
return fmt::format("var[{}]", _type.index());
return fmt::format("{}var{}", _type.generic() ? '?' : '\'', _type.index());
},
}, resolve(_type));
}
@ -180,6 +206,41 @@ experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
return result;
}
vector<experimental::Type> TypeSystemHelpers::destTupleType(Type _tupleType) const
{
auto [constructor, arguments] = destTypeExpression(_tupleType);
if (auto const* builtinType = get_if<BuiltinType>(&constructor))
{
if (*builtinType == BuiltinType::Unit)
return {};
else if (*builtinType != BuiltinType::Pair)
return {_tupleType};
}
else
return {_tupleType};
solAssert(arguments.size() == 2);
vector<Type> result;
result.emplace_back(arguments.front());
Type tail = arguments.back();
while(true)
{
auto const* tailTypeExpression = get_if<TypeExpression>(&tail);
if (!tailTypeExpression)
break;
auto [tailConstructor, tailArguments] = destTypeExpression(tail);
auto const* builtinType = get_if<BuiltinType>(&tailConstructor);
if(!builtinType || *builtinType != BuiltinType::Pair)
break;
solAssert(tailArguments.size() == 2);
result.emplace_back(tailArguments.front());
tail = tailArguments.back();
}
result.emplace_back(tail);
return result;
}
experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const
{
return typeSystem.builtinType(BuiltinType::Function, {_argType, _resultType});
@ -198,7 +259,6 @@ tuple<TypeExpression::Constructor, vector<experimental::Type>> TypeSystemHelpers
}, _functionType);
}
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
{
auto [constructor, arguments] = destTypeExpression(_functionType);

View File

@ -46,7 +46,8 @@ enum class BuiltinType
Function,
Unit,
Pair,
Word
Word,
Integer
};
struct TypeExpression
@ -104,10 +105,11 @@ private:
struct TypeSystemHelpers
{
TypeSystem& typeSystem;
Type tupleType(std::vector<Type> _elements) const;
Type functionType(Type _argType, Type _resultType) const;
TypeSystem const& typeSystem;
std::tuple<TypeExpression::Constructor, std::vector<Type>> destTypeExpression(Type _functionType) const;
Type tupleType(std::vector<Type> _elements) const;
std::vector<Type> destTupleType(Type _tupleType) const;
Type functionType(Type _argType, Type _resultType) const;
std::tuple<Type, Type> destFunctionType(Type _functionType) const;
};

View File

@ -134,6 +134,10 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
case Token::Function:
nodes.push_back(parseFunctionDefinition(true));
break;
case Token::Class:
solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit);
nodes.push_back(parseTypeClassDefinition());
break;
default:
if (
// Workaround because `error` is not a keyword.
@ -623,7 +627,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
return result;
}
ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction, bool _allowBody)
{
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
@ -673,7 +677,9 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition(bool _freeFunction)
ASTPointer<Block> block;
nodeFactory.markEndPosition();
if (m_scanner->currentToken() == Token::Semicolon)
if (!_allowBody)
expectToken(Token::Semicolon);
else if (m_scanner->currentToken() == Token::Semicolon)
advance();
else
{
@ -1270,6 +1276,13 @@ ASTPointer<ParameterList> Parser::parseParameterList(
vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options(_options);
options.allowEmptyName = true;
if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Identifier)
{
// Parses unary parameter lists without parentheses. TODO: is this a good idea in all cases? Including arguments?
parameters = {parsePostfixVariableDeclaration()};
nodeFactory.setEndPositionFromNode(parameters.front());
return nodeFactory.createNode<ParameterList>(parameters);
}
expectToken(Token::LParen);
auto parseSingleVariableDeclaration = [&]() {
if (m_experimentalSolidityEnabledInCurrentSourceUnit)
@ -1717,6 +1730,54 @@ ASTPointer<VariableDeclaration> Parser::parsePostfixVariableDeclaration()
);
}
ASTPointer<TypeClassDefinition> Parser::parseTypeClassDefinition()
{
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<ASTNode>> subNodes;
ASTPointer<StructuredDocumentation> const documentation = parseStructuredDocumentation();
expectToken(Token::Class);
// TODO: parseTypeVariable()? parseTypeVariableDeclaration()?
ASTPointer<VariableDeclaration> typeVariable;
{
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
auto [identifier, nameLocation] = expectIdentifierWithLocation();
typeVariable = nodeFactory.createNode<VariableDeclaration>(
nullptr,
identifier,
nameLocation,
nullptr,
Visibility::Default,
nullptr
);
}
expectToken(Token::Colon);
auto [name, nameLocation] = expectIdentifierWithLocation();
expectToken(Token::LBrace);
while (true)
{
Token currentTokenValue = m_scanner->currentToken();
if (currentTokenValue == Token::RBrace)
break;
expectToken(Token::Function, false);
subNodes.push_back(parseFunctionDefinition(false, false));
}
nodeFactory.markEndPosition();
expectToken(Token::RBrace);
return nodeFactory.createNode<TypeClassDefinition>(
typeVariable,
name,
nameLocation,
documentation,
subNodes
);
}
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
{
RecursionGuard recursionGuard(*this);

View File

@ -102,7 +102,7 @@ private:
ASTPointer<OverrideSpecifier> parseOverrideSpecifier();
StateMutability parseStateMutability();
FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable);
ASTPointer<ASTNode> parseFunctionDefinition(bool _freeFunction = false);
ASTPointer<ASTNode> parseFunctionDefinition(bool _freeFunction = false, bool _allowBody = true);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<UserDefinedValueTypeDefinition> parseUserDefinedValueTypeDefinition();
@ -176,6 +176,7 @@ private:
ASTPointer<ASTString> const& _docString
);
ASTPointer<VariableDeclaration> parsePostfixVariableDeclaration();
ASTPointer<TypeClassDefinition> parseTypeClassDefinition();
///@}
///@{

View File

@ -1,10 +1,10 @@
pragma experimental solidity;
/*
class a:StackType {
function stackSize() -> (integer);
}
class a:StackType {
function stackSize() -> x:integer;
}
/*
instantiate uint256 : StackType {
function stackSize() -> (integer) {
return 1;
@ -12,7 +12,7 @@ instantiate uint256 : StackType {
}
*/
function f(a) -> (b) {
function f(a) -> b {
return a;
}