Skip to content

Commit bdd2f02

Browse files
authored
Merge pull request #14566 from ethereum/new-analysis-defining-builtins
Defining built-ins in experimental analysis
2 parents 271d55c + 0819e31 commit bdd2f02

17 files changed

+287
-128
lines changed

Diff for: liblangutil/Token.h

+5-14
Original file line numberDiff line numberDiff line change
@@ -269,17 +269,13 @@ namespace solidity::langutil
269269
T(Leave, "leave", 0) \
270270
\
271271
T(NonExperimentalEnd, nullptr, 0) /* used as non-experimental enum end marker */ \
272-
/* Experimental Solidity specific keywords. */ \
272+
/* Experimental Solidity specific keywords. */ \
273273
K(Class, "class", 0) \
274274
K(Instantiation, "instantiation", 0) \
275-
K(Word, "word", 0) \
276-
K(Integer, "integer", 0) \
275+
K(Integer, "Integer", 0) \
277276
K(Itself, "itself", 0) \
278-
K(Void, "void", 0) \
279-
K(Pair, "pair", 0) \
280-
K(Fun, "fun", 0) \
281-
K(Unit, "unit", 0) \
282277
K(StaticAssert, "static_assert", 0) \
278+
K(Builtin, "__builtin", 0) \
283279
T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \
284280
\
285281
/* Illegal token - not able to scan. */ \
@@ -304,12 +300,7 @@ namespace TokenTraits
304300
constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); }
305301

306302
// Predicates
307-
constexpr bool isElementaryTypeName(Token tok)
308-
{
309-
return (Token::Int <= tok && tok < Token::TypesEnd) ||
310-
tok == Token::Word || tok == Token::Void || tok == Token::Integer ||
311-
tok == Token::Pair || tok == Token::Unit || tok == Token::Fun;
312-
}
303+
constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; }
313304
constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
314305
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }
315306
constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd ||
@@ -350,7 +341,7 @@ namespace TokenTraits
350341
{
351342
return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback ||
352343
tok == Token::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let ||
353-
tok == Token::Return || tok == Token::Type || tok == Token::Bool || tok == Token::If || tok == Token::Else ||
344+
tok == Token::Return || tok == Token::Type || tok == Token::If || tok == Token::Else ||
354345
tok == Token::Do || tok == Token::While || tok == Token::For || tok == Token::Continue || tok == Token::Break ||
355346
(tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd);
356347
}

Diff for: libsolidity/ast/AST.h

+29
Original file line numberDiff line numberDiff line change
@@ -2596,6 +2596,35 @@ class TypeClassName: public ASTNode
25962596
std::variant<Token, ASTPointer<IdentifierPath>> m_name;
25972597
};
25982598

2599+
class Builtin: public Expression
2600+
{
2601+
public:
2602+
Builtin(
2603+
int64_t _id,
2604+
SourceLocation _location,
2605+
ASTPointer<ASTString> _nameParameter,
2606+
SourceLocation _nameParameterLocation
2607+
):
2608+
Expression(_id, std::move(_location)),
2609+
m_nameParameter(std::move(_nameParameter)),
2610+
m_nameParameterLocation(std::move(_nameParameterLocation))
2611+
{
2612+
solAssert(m_nameParameter);
2613+
}
2614+
2615+
void accept(ASTVisitor& _visitor) override;
2616+
void accept(ASTConstVisitor& _visitor) const override;
2617+
2618+
bool experimentalSolidityOnly() const override { return true; }
2619+
2620+
ASTString const& nameParameter() const { return *m_nameParameter; }
2621+
SourceLocation const& nameParameterLocation() const { return m_nameParameterLocation; }
2622+
2623+
private:
2624+
ASTPointer<ASTString> m_nameParameter;
2625+
SourceLocation m_nameParameterLocation;
2626+
};
2627+
25992628
/// @}
26002629

26012630
}

Diff for: libsolidity/ast/ASTForward.h

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class TypeClassDefinition;
105105
class TypeClassInstantiation;
106106
class TypeClassName;
107107
class TypeDefinition;
108+
class Builtin;
108109
/// @}
109110

110111
class VariableScope;

Diff for: libsolidity/ast/ASTVisitor.h

+4
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class ASTVisitor
115115
virtual bool visit(TypeClassInstantiation& _node) { return visitNode(_node); }
116116
virtual bool visit(TypeDefinition& _node) { return visitNode(_node); }
117117
virtual bool visit(TypeClassName& _node) { return visitNode(_node); }
118+
virtual bool visit(Builtin& _node) { return visitNode(_node); }
118119
/// @}
119120

120121
virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); }
@@ -178,6 +179,7 @@ class ASTVisitor
178179
virtual void endVisit(TypeClassInstantiation& _node) { endVisitNode(_node); }
179180
virtual void endVisit(TypeDefinition& _node) { endVisitNode(_node); }
180181
virtual void endVisit(TypeClassName& _node) { endVisitNode(_node); }
182+
virtual void endVisit(Builtin& _node) { endVisitNode(_node); }
181183
/// @}
182184

183185
protected:
@@ -263,6 +265,7 @@ class ASTConstVisitor
263265
virtual bool visit(TypeClassInstantiation const& _node) { return visitNode(_node); }
264266
virtual bool visit(TypeDefinition const& _node) { return visitNode(_node); }
265267
virtual bool visit(TypeClassName const& _node) { return visitNode(_node); }
268+
virtual bool visit(Builtin const& _node) { return visitNode(_node); }
266269
/// @}
267270

268271
virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); }
@@ -326,6 +329,7 @@ class ASTConstVisitor
326329
virtual void endVisit(TypeClassInstantiation const& _node) { endVisitNode(_node); }
327330
virtual void endVisit(TypeDefinition const& _node) { endVisitNode(_node); }
328331
virtual void endVisit(TypeClassName const& _node) { endVisitNode(_node); }
332+
virtual void endVisit(Builtin const& _node) { endVisitNode(_node); }
329333
/// @}
330334

331335
protected:

Diff for: libsolidity/ast/AST_accept.h

+12
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,18 @@ void TypeClassName::accept(ASTConstVisitor& _visitor) const
11281128
}
11291129
_visitor.endVisit(*this);
11301130
}
1131+
1132+
void Builtin::accept(ASTVisitor& _visitor)
1133+
{
1134+
_visitor.visit(*this);
1135+
_visitor.endVisit(*this);
1136+
}
1137+
1138+
void Builtin::accept(ASTConstVisitor& _visitor) const
1139+
{
1140+
_visitor.visit(*this);
1141+
_visitor.endVisit(*this);
1142+
}
11311143
/// @}
11321144

11331145
}

Diff for: libsolidity/experimental/analysis/TypeInference.cpp

+24-66
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,6 @@ TypeInference::TypeInference(Analysis& _analysis):
125125
defineBinaryCompareOperator(BuiltinClass::LessOrEqual, Token::LessThanOrEqual, "leq");
126126
defineBinaryCompareOperator(BuiltinClass::Greater, Token::GreaterThan, "gt");
127127
defineBinaryCompareOperator(BuiltinClass::GreaterOrEqual, Token::GreaterThanOrEqual, "geq");
128-
129-
{
130-
auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Bool), std::map<std::string, TypeMember>{});
131-
solAssert(newlyInserted);
132-
members->second.emplace("abs", TypeMember{helper.functionType(m_wordType, m_boolType)});
133-
members->second.emplace("rep", TypeMember{helper.functionType(m_boolType, m_wordType)});
134-
}
135-
{
136-
Type first = m_typeSystem.freshTypeVariable({});
137-
Type second = m_typeSystem.freshTypeVariable({});
138-
Type pairType = m_typeSystem.type(PrimitiveType::Pair, {first, second});
139-
auto [members, newlyInserted] = annotation().members.emplace(m_typeSystem.constructor(PrimitiveType::Pair), std::map<std::string, TypeMember>{});
140-
solAssert(newlyInserted);
141-
members->second.emplace("first", TypeMember{helper.functionType(pairType, first)});
142-
members->second.emplace("second", TypeMember{helper.functionType(pairType, second)});
143-
}
144128
}
145129

146130
bool TypeInference::analyze(SourceUnit const& _sourceUnit)
@@ -307,48 +291,6 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
307291
return false;
308292
}
309293

310-
bool TypeInference::visit(ElementaryTypeNameExpression const& _expression)
311-
{
312-
auto& expressionAnnotation = annotation(_expression);
313-
solAssert(!expressionAnnotation.type);
314-
315-
switch (m_expressionContext)
316-
{
317-
case ExpressionContext::Term:
318-
case ExpressionContext::Type:
319-
if (auto constructor = m_analysis.annotation<TypeRegistration>(_expression).typeConstructor)
320-
{
321-
std::vector<Type> arguments;
322-
std::generate_n(std::back_inserter(arguments), m_typeSystem.constructorInfo(*constructor).arguments(), [&]() {
323-
return m_typeSystem.freshTypeVariable({});
324-
});
325-
// TODO: get rid of the distinction (assign a function on unit for the empty case)? Ambiguity?
326-
if (arguments.empty() || m_expressionContext == ExpressionContext::Term)
327-
expressionAnnotation.type = m_typeSystem.type(*constructor, arguments);
328-
else
329-
{
330-
TypeSystemHelpers helper{m_typeSystem};
331-
expressionAnnotation.type =
332-
helper.typeFunctionType(
333-
helper.tupleType(arguments),
334-
m_typeSystem.type(*constructor, arguments)
335-
);
336-
}
337-
}
338-
else
339-
{
340-
m_errorReporter.typeError(4107_error, _expression.location(), "No type constructor registered for elementary type name.");
341-
expressionAnnotation.type = m_typeSystem.freshTypeVariable({});
342-
}
343-
break;
344-
case ExpressionContext::Sort:
345-
m_errorReporter.typeError(2024_error, _expression.location(), "Elementary type name expression not supported in sort context.");
346-
expressionAnnotation.type = m_typeSystem.freshTypeVariable({});
347-
break;
348-
}
349-
return false;
350-
}
351-
352294
bool TypeInference::visit(BinaryOperation const& _binaryOperation)
353295
{
354296
auto& operationAnnotation = annotation(_binaryOperation);
@@ -825,6 +767,8 @@ void TypeInference::endVisit(MemberAccess const& _memberAccess)
825767

826768
bool TypeInference::visit(TypeDefinition const& _typeDefinition)
827769
{
770+
bool isBuiltIn = dynamic_cast<Builtin const*>(_typeDefinition.typeExpression());
771+
828772
TypeSystemHelpers helper{m_typeSystem};
829773
auto& typeDefinitionAnnotation = annotation(_typeDefinition);
830774
if (typeDefinitionAnnotation.type)
@@ -833,14 +777,6 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition)
833777
if (_typeDefinition.arguments())
834778
_typeDefinition.arguments()->accept(*this);
835779

836-
std::optional<Type> underlyingType;
837-
if (_typeDefinition.typeExpression())
838-
{
839-
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
840-
_typeDefinition.typeExpression()->accept(*this);
841-
underlyingType = annotation(*_typeDefinition.typeExpression()).type;
842-
}
843-
844780
std::vector<Type> arguments;
845781
if (_typeDefinition.arguments())
846782
for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i)
@@ -852,6 +788,19 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition)
852788
else
853789
typeDefinitionAnnotation.type = helper.typeFunctionType(helper.tupleType(arguments), definedType);
854790

791+
std::optional<Type> underlyingType;
792+
793+
if (isBuiltIn)
794+
// TODO: This special case should eventually become user-definable.
795+
underlyingType = m_wordType;
796+
else if (_typeDefinition.typeExpression())
797+
{
798+
ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type};
799+
_typeDefinition.typeExpression()->accept(*this);
800+
801+
underlyingType = annotation(*_typeDefinition.typeExpression()).type;
802+
}
803+
855804
TypeConstructor constructor = typeConstructor(&_typeDefinition);
856805
auto [members, newlyInserted] = annotation().members.emplace(constructor, std::map<std::string, TypeMember>{});
857806
solAssert(newlyInserted, fmt::format("Members of type '{}' are already defined.", m_typeSystem.constructorInfo(constructor).name));
@@ -860,6 +809,15 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition)
860809
members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, definedType)});
861810
members->second.emplace("rep", TypeMember{helper.functionType(definedType, *underlyingType)});
862811
}
812+
813+
if (helper.isPrimitiveType(definedType, PrimitiveType::Pair))
814+
{
815+
solAssert(isBuiltIn);
816+
solAssert(arguments.size() == 2);
817+
members->second.emplace("first", TypeMember{helper.functionType(definedType, arguments[0])});
818+
members->second.emplace("second", TypeMember{helper.functionType(definedType, arguments[1])});
819+
}
820+
863821
return false;
864822
}
865823

Diff for: libsolidity/experimental/analysis/TypeInference.h

-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ class TypeInference: public ASTConstVisitor
7979

8080
bool visit(MemberAccess const& _memberAccess) override;
8181
void endVisit(MemberAccess const& _memberAccess) override;
82-
bool visit(ElementaryTypeNameExpression const& _expression) override;
8382

8483
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
8584
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;

Diff for: libsolidity/experimental/analysis/TypeRegistration.cpp

+57-33
Original file line numberDiff line numberDiff line change
@@ -56,32 +56,30 @@ bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition)
5656
return true;
5757
}
5858

59-
bool TypeRegistration::visit(ElementaryTypeName const& _typeName)
59+
bool TypeRegistration::visit(Builtin const& _builtin)
6060
{
61-
if (annotation(_typeName).typeConstructor)
61+
if (annotation(_builtin).typeConstructor)
6262
return false;
63-
annotation(_typeName).typeConstructor = [&]() -> std::optional<TypeConstructor> {
64-
switch (_typeName.typeName().token())
65-
{
66-
case Token::Void:
67-
return m_typeSystem.constructor(PrimitiveType::Void);
68-
case Token::Fun:
69-
return m_typeSystem.constructor(PrimitiveType::Function);
70-
case Token::Unit:
71-
return m_typeSystem.constructor(PrimitiveType::Unit);
72-
case Token::Pair:
73-
return m_typeSystem.constructor(PrimitiveType::Pair);
74-
case Token::Word:
75-
return m_typeSystem.constructor(PrimitiveType::Word);
76-
case Token::Integer:
77-
return m_typeSystem.constructor(PrimitiveType::Integer);
78-
case Token::Bool:
79-
return m_typeSystem.constructor(PrimitiveType::Bool);
80-
default:
81-
m_errorReporter.fatalTypeError(7758_error, _typeName.location(), "Expected primitive type.");
82-
return std::nullopt;
83-
}
84-
}();
63+
64+
auto primitiveType = [&](std::string _name) -> std::optional<PrimitiveType> {
65+
if (_name == "void") return PrimitiveType::Void;
66+
if (_name == "fun") return PrimitiveType::Function;
67+
if (_name == "unit") return PrimitiveType::Unit;
68+
if (_name == "pair") return PrimitiveType::Pair;
69+
if (_name == "word") return PrimitiveType::Word;
70+
if (_name == "integer") return PrimitiveType::Integer;
71+
if (_name == "bool") return PrimitiveType::Bool;
72+
return std::nullopt;
73+
}(_builtin.nameParameter());
74+
75+
if (!primitiveType.has_value())
76+
m_errorReporter.fatalTypeError(
77+
7758_error,
78+
_builtin.location(),
79+
"Expected the name of a built-in primitive type."
80+
);
81+
82+
annotation(_builtin).typeConstructor = m_typeSystem.constructor(primitiveType.value());
8583
return true;
8684
}
8785

@@ -165,15 +163,41 @@ bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiati
165163

166164
bool TypeRegistration::visit(TypeDefinition const& _typeDefinition)
167165
{
168-
if (annotation(_typeDefinition).typeConstructor)
169-
return false;
170-
annotation(_typeDefinition).typeConstructor = m_typeSystem.declareTypeConstructor(
171-
_typeDefinition.name(),
172-
"t_" + *_typeDefinition.annotation().canonicalName + "_" + util::toString(_typeDefinition.id()),
173-
_typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0,
174-
&_typeDefinition
175-
);
176-
return true;
166+
return !annotation(_typeDefinition).typeConstructor.has_value();
167+
}
168+
169+
void TypeRegistration::endVisit(TypeDefinition const& _typeDefinition)
170+
{
171+
if (annotation(_typeDefinition).typeConstructor.has_value())
172+
return;
173+
174+
if (auto const* builtin = dynamic_cast<Builtin const*>(_typeDefinition.typeExpression()))
175+
{
176+
auto [previousDefinitionIt, inserted] = annotation().builtinTypeDefinitions.try_emplace(
177+
builtin->nameParameter(),
178+
&_typeDefinition
179+
);
180+
181+
if (inserted)
182+
annotation(_typeDefinition).typeConstructor = annotation(*builtin).typeConstructor;
183+
else
184+
{
185+
auto const& [builtinName, previousDefinition] = *previousDefinitionIt;
186+
m_errorReporter.typeError(
187+
9609_error,
188+
_typeDefinition.location(),
189+
SecondarySourceLocation{}.append("Previous definition:", previousDefinition->location()),
190+
"Duplicate builtin type definition."
191+
);
192+
}
193+
}
194+
else
195+
annotation(_typeDefinition).typeConstructor = m_typeSystem.declareTypeConstructor(
196+
_typeDefinition.name(),
197+
"t_" + *_typeDefinition.annotation().canonicalName + "_" + util::toString(_typeDefinition.id()),
198+
_typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0,
199+
&_typeDefinition
200+
);
177201
}
178202

179203
TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node)

0 commit comments

Comments
 (0)