Skip to content

Commit 68bb271

Browse files
committed
Initial support for variable templates
Signed-off-by: Roberto Raggi <[email protected]>
1 parent 4ffe0aa commit 68bb271

File tree

9 files changed

+168
-8
lines changed

9 files changed

+168
-8
lines changed

src/parser/cxx/ast_rewriter.cc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,68 @@ auto ASTRewriter::instantiateTypeAliasTemplate(
201201
return instance->symbol;
202202
}
203203

204+
auto ASTRewriter::instantiateVariableTemplate(
205+
TranslationUnit* unit, List<TemplateArgumentAST*>* templateArgumentList,
206+
VariableSymbol* variableSymbol) -> VariableSymbol* {
207+
auto templateDecl = variableSymbol->templateDeclaration();
208+
209+
if (!templateDecl) {
210+
unit->error(variableSymbol->location(), "not a template");
211+
return nullptr;
212+
}
213+
214+
auto variableDeclaration =
215+
ast_cast<SimpleDeclarationAST>(templateDecl->declaration);
216+
217+
if (!variableDeclaration) return nullptr;
218+
219+
auto templateArguments =
220+
make_substitution(unit, templateDecl, templateArgumentList);
221+
222+
auto is_primary_template = [&]() -> bool {
223+
int expected = 0;
224+
for (const auto& arg : templateArguments) {
225+
if (!std::holds_alternative<Symbol*>(arg)) return false;
226+
227+
auto ty = type_cast<TypeParameterType>(std::get<Symbol*>(arg)->type());
228+
if (!ty) return false;
229+
230+
if (ty->index() != expected) return false;
231+
++expected;
232+
}
233+
return true;
234+
};
235+
236+
if (is_primary_template()) {
237+
// if this is a primary template, we can just return the class symbol
238+
return variableSymbol;
239+
}
240+
241+
auto subst = variableSymbol->findSpecialization(templateArguments);
242+
if (subst) {
243+
return subst;
244+
}
245+
246+
auto parentScope = variableSymbol->parent();
247+
while (parentScope->isTemplateParameters()) {
248+
parentScope = parentScope->parent();
249+
}
250+
251+
auto rewriter = ASTRewriter{unit, parentScope, templateArguments};
252+
253+
rewriter.binder().setInstantiatingSymbol(variableSymbol);
254+
255+
auto instance =
256+
ast_cast<SimpleDeclarationAST>(rewriter.declaration(variableDeclaration));
257+
258+
if (!instance) return nullptr;
259+
260+
auto instantiatedSymbol = instance->initDeclaratorList->value->symbol;
261+
auto instantiatedVariable = symbol_cast<VariableSymbol>(instantiatedSymbol);
262+
263+
return instantiatedVariable;
264+
}
265+
204266
auto ASTRewriter::make_substitution(
205267
TranslationUnit* unit, TemplateDeclarationAST* templateDecl,
206268
List<TemplateArgumentAST*>* templateArgumentList)

src/parser/cxx/ast_rewriter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class ASTRewriter {
4242
TranslationUnit* unit, List<TemplateArgumentAST*>* templateArgumentList,
4343
TypeAliasSymbol* typeAliasSymbol) -> TypeAliasSymbol*;
4444

45+
[[nodiscard]] static auto instantiateVariableTemplate(
46+
TranslationUnit* unit, List<TemplateArgumentAST*>* templateArgumentList,
47+
VariableSymbol* variableSymbol) -> VariableSymbol*;
48+
4549
explicit ASTRewriter(TranslationUnit* unit, ScopeSymbol* scope,
4650
const std::vector<TemplateArgument>& templateArguments);
4751
~ASTRewriter();

src/parser/cxx/ast_rewriter_declarators.cc

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
// cxx
2424
#include <cxx/ast.h>
25+
#include <cxx/ast_interpreter.h>
2526
#include <cxx/binder.h>
2627
#include <cxx/decl.h>
2728
#include <cxx/decl_specs.h>
@@ -208,6 +209,9 @@ auto ASTRewriter::initDeclarator(InitDeclaratorAST* ast,
208209
auto type =
209210
getDeclaratorType(translationUnit(), copy->declarator, declSpecs.type());
210211

212+
const auto addSymbolToParentScope =
213+
binder().instantiatingSymbol() != ast->symbol;
214+
211215
// ### fix scope
212216
if (binder_.scope()->isClass()) {
213217
auto symbol = binder_.declareMemberSymbol(copy->declarator, decl);
@@ -222,16 +226,30 @@ auto ASTRewriter::initDeclarator(InitDeclaratorAST* ast,
222226
auto functionSymbol = binder_.declareFunction(copy->declarator, decl);
223227
copy->symbol = functionSymbol;
224228
} else {
225-
auto variableSymbol = binder_.declareVariable(copy->declarator, decl);
229+
auto variableSymbol = binder_.declareVariable(copy->declarator, decl,
230+
addSymbolToParentScope);
226231
// variableSymbol->setTemplateDeclaration(templateHead);
227232
copy->symbol = variableSymbol;
233+
234+
if (!addSymbolToParentScope) {
235+
auto templateVariable = symbol_cast<VariableSymbol>(ast->symbol);
236+
templateVariable->addSpecialization(templateArguments(),
237+
variableSymbol);
238+
}
228239
}
229240
}
230241
}
231242

232243
copy->requiresClause = requiresClause(ast->requiresClause);
233244
copy->initializer = expression(ast->initializer);
234-
// copy->symbol = ast->symbol; // TODO remove, done above
245+
246+
if (auto variableSymbol = symbol_cast<VariableSymbol>(copy->symbol)) {
247+
if (variableSymbol->isConstexpr()) {
248+
auto interp = ASTInterpreter{unit_};
249+
auto constValue = interp.evaluate(copy->initializer);
250+
variableSymbol->setConstValue(constValue);
251+
}
252+
}
235253

236254
return copy;
237255
}

src/parser/cxx/binder.cc

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -818,15 +818,17 @@ auto Binder::declareField(DeclaratorAST* declarator, const Decl& decl)
818818
return fieldSymbol;
819819
}
820820

821-
auto Binder::declareVariable(DeclaratorAST* declarator, const Decl& decl)
822-
-> VariableSymbol* {
821+
auto Binder::declareVariable(DeclaratorAST* declarator, const Decl& decl,
822+
bool addSymbolToParentScope) -> VariableSymbol* {
823823
auto name = decl.getName();
824824
auto symbol = control()->newVariableSymbol(scope(), decl.location());
825825
auto type = getDeclaratorType(unit_, declarator, decl.specs.type());
826826
applySpecifiers(symbol, decl.specs);
827827
symbol->setName(name);
828828
symbol->setType(type);
829-
declaringScope()->addSymbol(symbol);
829+
if (addSymbolToParentScope) {
830+
declaringScope()->addSymbol(symbol);
831+
}
830832
return symbol;
831833
}
832834

@@ -963,6 +965,21 @@ void Binder::bind(IdExpressionAST* ast) {
963965
}
964966

965967
ast->symbol = Lookup{scope()}(ast->nestedNameSpecifier, componentName);
968+
969+
if (unit_->config().checkTypes) {
970+
if (auto templateId = ast_cast<SimpleTemplateIdAST>(ast->unqualifiedId)) {
971+
auto var = symbol_cast<VariableSymbol>(ast->symbol);
972+
973+
if (!var) {
974+
error(templateId->firstSourceLocation(), std::format("not a template"));
975+
} else {
976+
auto instance = ASTRewriter::instantiateVariableTemplate(
977+
unit_, templateId->templateArgumentList, var);
978+
979+
ast->symbol = instance;
980+
}
981+
}
982+
}
966983
}
967984

968985
auto Binder::getFunction(ScopeSymbol* scope, const Name* name, const Type* type)

src/parser/cxx/binder.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ class Binder {
8282
-> FieldSymbol*;
8383

8484
[[nodiscard]] auto declareVariable(DeclaratorAST* declarator,
85-
const Decl& decl) -> VariableSymbol*;
85+
const Decl& decl,
86+
bool addSymbolToParentScope)
87+
-> VariableSymbol*;
8688

8789
[[nodiscard]] auto declareMemberSymbol(DeclaratorAST* declarator,
8890
const Decl& decl) -> Symbol*;

src/parser/cxx/parser.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,7 +3348,8 @@ void Parser::parse_condition(ExpressionAST*& yyast, const ExprContext& ctx) {
33483348

33493349
if (!parse_declarator(declarator, decl)) return false;
33503350

3351-
auto symbol = binder_.declareVariable(declarator, decl);
3351+
auto symbol = binder_.declareVariable(declarator, decl,
3352+
/*addSymbolToParentScope=*/true);
33523353

33533354
ExpressionAST* initializer = nullptr;
33543355

@@ -5573,7 +5574,8 @@ auto Parser::parse_init_declarator(InitDeclaratorAST*& yyast,
55735574
auto functionSymbol = binder_.declareFunction(declarator, decl);
55745575
symbol = functionSymbol;
55755576
} else {
5576-
auto variableSymbol = binder_.declareVariable(declarator, decl);
5577+
auto variableSymbol = binder_.declareVariable(
5578+
declarator, decl, /*addSymbolToParentScope=*/true);
55775579
variableSymbol->setTemplateDeclaration(templateHead);
55785580
symbol = variableSymbol;
55795581
}

src/parser/cxx/symbols.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,28 @@ void VariableSymbol::setTemplateDeclaration(
769769
templateDeclaration_ = declaration;
770770
}
771771

772+
auto VariableSymbol::specializations() const
773+
-> std::span<const TemplateSpecialization<VariableSymbol>> {
774+
if (!templateInfo_) return {};
775+
return templateInfo_->specializations();
776+
}
777+
778+
auto VariableSymbol::findSpecialization(
779+
const std::vector<TemplateArgument>& arguments) const -> VariableSymbol* {
780+
if (!templateInfo_) return {};
781+
return templateInfo_->findSpecialization(arguments);
782+
}
783+
784+
void VariableSymbol::addSpecialization(std::vector<TemplateArgument> arguments,
785+
VariableSymbol* specialization) {
786+
if (!templateInfo_) {
787+
templateInfo_ = std::make_unique<TemplateInfo<VariableSymbol>>(this);
788+
}
789+
auto index = templateInfo_->specializations().size();
790+
specialization->setSpecializationInfo(this, index);
791+
templateInfo_->addSpecialization(std::move(arguments), specialization);
792+
}
793+
772794
auto VariableSymbol::initializer() const -> ExpressionAST* {
773795
return initializer_;
774796
}

src/parser/cxx/symbols.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,28 @@ class VariableSymbol final : public Symbol {
595595
[[nodiscard]] auto templateDeclaration() const -> TemplateDeclarationAST*;
596596
void setTemplateDeclaration(TemplateDeclarationAST* declaration);
597597

598+
[[nodiscard]] auto specializations() const
599+
-> std::span<const TemplateSpecialization<VariableSymbol>>;
600+
601+
[[nodiscard]] auto findSpecialization(
602+
const std::vector<TemplateArgument>& arguments) const -> VariableSymbol*;
603+
604+
void addSpecialization(std::vector<TemplateArgument> arguments,
605+
VariableSymbol* specialization);
606+
607+
void setSpecializationInfo(VariableSymbol* templateVariable,
608+
std::size_t index) {
609+
templateVariable_ = templateVariable;
610+
templateSepcializationIndex_ = index;
611+
}
612+
613+
[[nodiscard]] auto templateArguments() const
614+
-> std::span<const TemplateArgument> {
615+
if (!templateVariable_) return {};
616+
return templateVariable_->specializations()[templateSepcializationIndex_]
617+
.arguments;
618+
}
619+
598620
[[nodiscard]] auto initializer() const -> ExpressionAST*;
599621
void setInitializer(ExpressionAST*);
600622

@@ -605,6 +627,9 @@ class VariableSymbol final : public Symbol {
605627
TemplateDeclarationAST* templateDeclaration_ = nullptr;
606628
ExpressionAST* initializer_ = nullptr;
607629
std::optional<ConstValue> constValue_;
630+
std::unique_ptr<TemplateInfo<VariableSymbol>> templateInfo_;
631+
VariableSymbol* templateVariable_ = nullptr;
632+
std::size_t templateSepcializationIndex_ = 0;
608633

609634
union {
610635
std::uint32_t flags_{};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %cxx -verify -fcheck -dump-symbols %s
2+
3+
template <typename T>
4+
constexpr bool is_integral_v = __is_integral(T);
5+
6+
static_assert(is_integral_v<int>);
7+
static_assert(is_integral_v<char>);
8+
static_assert(is_integral_v<void*> == false);

0 commit comments

Comments
 (0)