Skip to content

Commit db0b337

Browse files
committed
language: improve template handling
* accept alternate template syntax: template<T, N=0> definition, template(T, N=0) definition * support template block definitions: template<T> { definition ... } * support multiple template argument syntax * simplify function declaration builders * add TemplateExpr and TemplateSpec ast nodes * simplify and extend templated function call parsing * handle struct templates (at last) * add template samples
1 parent 9817258 commit db0b337

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1893
-516
lines changed

analyser/conversion_checker_expr.c2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ fn ExprWidth getExprWidth(const Expr* e) {
6464
return getTypeWidth(e.getType());
6565
case Type:
6666
break;
67+
case Template:
6768
case Call:
6869
return getTypeWidth(e.getType());
6970
case InitList:

analyser/module_analyser_call.c2

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,31 @@ const char[] DiagTooManyArgs = "too many arguments to %sfunction call, expected
2929
const char[] DiagTooFewArgs = "too few arguments to %sfunction call, expected %d, have %d";
3030
const char[] NoteDeclaredHere = "'%s' is defined here";
3131

32+
fn QualType Analyser.analyseTemplateExpr(Analyser* ma, Expr** e_ptr) {
33+
Expr* e = *e_ptr;
34+
TemplateExpr* tp = (TemplateExpr*)e;
35+
Expr** func = tp.getFunc2();
36+
Expr* origFn = tp.getFunc(); // store here to avoid the likely inserted FunctionPointerDecay cast
37+
QualType qt = ma.analyseExpr(func, true, RHS);
38+
if (qt.isInvalid()) return QualType_Invalid;
39+
FunctionType* ft = qt.getFunctionTypeOrNil();
40+
if (!ft) {
41+
ma.errorRange(origFn.getLoc(), origFn.getRange(), "object type %s is not a function template", qt.diagName());
42+
return QualType_Invalid;
43+
}
44+
FunctionDecl* fd = ft.getDecl();
45+
if (!fd.isTemplate() || fd.isTemplateTypeFunction()) {
46+
ma.errorRange(e.getLoc(), e.getRange(), "function %s is not a template function", fd.asDecl().getFullName());
47+
return QualType_Invalid;
48+
}
49+
50+
FunctionDecl* fd2 = ma.instantiateFunctionTemplate(fd, tp.getInstanceASTIdx(), tp.getArgs(), tp.getNumArgs());
51+
if (!fd2) return QualType_Invalid;
52+
fd2.asDecl().setUsed();
53+
tp.setDecl(fd2.asDecl());
54+
return fd2.asDecl().getType();
55+
}
56+
3257
fn QualType Analyser.analyseCallExpr(Analyser* ma, Expr** e_ptr) {
3358
Expr* e = *e_ptr;
3459
CallExpr* call = (CallExpr*)e;
@@ -84,17 +109,10 @@ fn QualType Analyser.analyseCallExpr(Analyser* ma, Expr** e_ptr) {
84109
if (fd.hasAttrNoReturn()) call.setNoreturn();
85110

86111
if (fd.isTemplate()) {
87-
if (!call.getTemplateArg()) {
88-
ma.errorRange(e.getLoc(), e.getRange(), "function %s requires a template argument", fd.asDecl().getFullName());
89-
return QualType_Invalid;
90-
}
91-
fd = ma.instantiateTemplateFunction(call, fd);
92-
if (!fd) return QualType_Invalid;
93-
} else {
94-
if (call.getTemplateArg()) {
95-
ma.errorRange(e.getLoc(), e.getRange(), "function %s is not a template function", fd.asDecl().getFullName());
96-
return QualType_Invalid;
97-
}
112+
// TODO handle default type arguments
113+
// TODO auto-instantiate based on actual argument types
114+
ma.errorRange(e.getLoc(), e.getRange(), "function %s requires a template argument", fd.asDecl().getFullName());
115+
return QualType_Invalid;
98116
}
99117

100118
if (fd.hasAttrDeprecated() && !ma.warnings.no_deprecated) {
@@ -659,12 +677,18 @@ fn void Analyser.opaque_callback(void* arg, SrcLoc loc, Decl* d) {
659677
ma.error(loc," using opaque type '%s'", qt.diagName());
660678
}
661679

662-
fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* call, FunctionDecl* fd) {
663-
TypeRef* template_arg = call.getTemplateArg();
680+
fn FunctionDecl* Analyser.instantiateFunctionTemplate(Analyser* ma, FunctionDecl* fd, u16 instance_ast_idx, Expr** args, u32 num_args) {
681+
// Only handle first argument for now, convert to type
682+
TypeRef* template_arg = ma.getTemplateArg(*args);
683+
if (!template_arg) return nil;
664684
QualType templateType = ma.analyseTypeRef(template_arg);
665685
if (templateType.isInvalid()) return nil;
666686

667-
FunctionDecl* instance = ma.mod.findInstance(fd, templateType);
687+
// TODO create instiator, analyse/evaluate template expressions,
688+
// initialize TemplateSubst array from values or qualtypes,
689+
// compute instance name and check if name exists already
690+
691+
FunctionDecl* instance = (FunctionDecl*)ma.mod.findInstance(fd.asDecl(), templateType);
668692
if (!instance) {
669693
// note: template_arg decl is set here
670694
bool used_opaque = false;
@@ -674,11 +698,14 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca
674698
Decl* d = (Decl*)std;
675699
used_opaque = (std.isOpaque() && d.getModule() != ma.mod);
676700
}
701+
// TODO: use TemplateSpec in Instantiator
702+
const TemplateSpec* spec = fd.getTemplateSpec();
677703
Instantiator inst = {
678704
.c = ma.context,
679705
.ref = template_arg,
680-
.template_name = fd.getTemplateNameIdx(),
681-
.instance_ast_idx = call.getInstanceASTIdx(),
706+
.template_vars = spec.getTemplateVars(),
707+
.template_len = spec.getTemplateLen(),
708+
.instance_ast_idx = instance_ast_idx,
682709
.used_opaque = used_opaque,
683710
.arg = ma,
684711
.on_error = Analyser.opaque_callback,
@@ -689,6 +716,13 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca
689716
if (ma.has_error) return nil;
690717
d.setChecked();
691718

719+
// add instance to avoid recursive instantiation for the same type
720+
u16 instance_idx = ma.mod.addInstance(fd.asDecl(), templateType, instance.asDecl());
721+
char[64] name;
722+
// TODO: use a more readable name, eg: max$i32
723+
create_template_name(name, elemsof(name), fd.asDecl().getName(), instance_idx);
724+
d.setNameIdx(ma.astPool.addStr(name, true));
725+
692726
// Note: we need a separate scope for the body
693727
Module* template_mod = fd.asDecl().getModule();
694728
Analyser* analyser = create(ma.diags, ma.context, ma.astPool, ma.builder, ma.allmodules, ma.warnings, ma.check_only);
@@ -704,15 +738,7 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca
704738
analyser.free();
705739

706740
if (ma.has_error) return nil;
707-
708-
u16 instance_idx = ma.mod.addInstance(fd, templateType, instance);
709-
instance.setTemplateInstanceIdx(instance_idx);
710-
char[64] name;
711-
create_template_name(name, elemsof(name), d.getName(), instance_idx);
712-
instance.setInstanceName(ma.astPool.addStr(name, true));
713741
}
714-
call.setTemplateIdx(instance.getTemplateInstanceIdx());
715-
716742
return instance;
717743
}
718744

analyser/module_analyser_expr.c2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ fn QualType Analyser.analyseExprInner(Analyser* ma, Expr** e_ptr, u32 side) {
7878
return d.getType();
7979
case Type:
8080
break;
81+
case Template:
82+
return ma.analyseTemplateExpr(e_ptr);
8183
case Call:
8284
return ma.analyseCallExpr(e_ptr);
8385
case InitList:

analyser/module_analyser_function.c2

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,15 @@ import scope;
2222
// Note: only analyses prototype + args, not the function body
2323
fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
2424
if (fd.isTemplate()) {
25-
// do analyze template name (eg X) for name clash
26-
ma.scope.checkGlobalSymbol(fd.getTemplateNameIdx(), fd.getTemplateLoc());
25+
// do analyze template names (eg X) for name clash
26+
const TemplateSpec* spec = fd.getTemplateSpec();
27+
if (spec) {
28+
u32 len = spec.getTemplateLen();
29+
const TemplateVar* vars = spec.getTemplateVars();
30+
for (u32 i = 0; i < len; i++) {
31+
ma.scope.checkGlobalSymbol(vars[i].name, vars[i].loc);
32+
}
33+
}
2734

2835
// check rtype for array-type
2936
TypeRef* rtype = fd.getReturnTypeRef();
@@ -128,16 +135,15 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
128135
bool is_typefn = false;
129136
if (num_params && fd.hasPrefix()) {
130137
// check if SF if first arg is (const) Struct* or (const) Struct
131-
// Note: use TypeRef, since it's faster to check
132-
const Ref* prefix = fd.getPrefix();
133-
const Decl* pd = prefix.decl;
134-
assert(pd);
135-
QualType prefixType = pd.getType();
136-
137-
TypeRef* ref = params[0].getTypeRef();
138-
const Ref* param_ref = ref.getUser();
139-
// Note: for enum types it can be the Type or a pointer to that type
140-
bool is_non_static = ((param_ref && param_ref.decl == prefix.decl) || ref.isPointerTo(prefixType.getIndex()));
138+
// Note: use TypeRef, since it's simpler to check
139+
const Ref* func_prefix = fd.getPrefix();
140+
const TypeRef* arg_ref = params[0].getTypeRef();
141+
const Ref* user = arg_ref.getUser();
142+
bool is_non_static = (user &&
143+
user.name_idx == func_prefix.name_idx &&
144+
arg_ref.getNumPointers() <= 1 &&
145+
!arg_ref.hasPrefix() &&
146+
!arg_ref.isTemplate());
141147
if (is_non_static) {
142148
fd.setCallKind(TypeFunc);
143149
is_typefn = true;

analyser/module_analyser_rewrite.c2

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,7 @@ fn void Analyser.rewriteCall(Analyser* ma, Expr** e_ptr, FunctionDecl* fd, Expr*
164164
func2 = origFn;
165165
}
166166

167-
Expr* call2;
168-
if (call.isTemplateCall()) {
169-
TypeRefHolder ref = call.getTemplateArg().getHolder();
170-
// TODO is builder.ast_idx correct?
171-
call2 = ma.builder.actOnTemplateCallExpr(0, 0, func2, args.getExprs(), args.size(), &ref);
172-
} else {
173-
call2 = ma.builder.actOnCallExpr(0, 0, func2, args.getExprs(), args.size());
174-
}
167+
Expr* call2 = ma.builder.actOnCallExpr(0, 0, func2, args.getExprs(), args.size());
175168
// Note: the type is only set on AlternateExpr by analyseExpr()
176169
e.setType(fd.getRType());
177170
call2.copyFlags(e);

analyser/module_analyser_struct.c2

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ import name_vector local;
2020
import size_analyser;
2121

2222
fn void Analyser.analyseStructType(Analyser* ma, StructTypeDecl* d) {
23+
if (d.isTemplate()) {
24+
// do analyze template names (eg X) for name clash
25+
const TemplateSpec* spec = d.getTemplateSpec();
26+
u32 len = spec.getTemplateLen();
27+
const TemplateVar* vars = spec.getTemplateVars();
28+
for (u32 i = 0; i < len; i++) {
29+
ma.scope.checkGlobalSymbol(vars[i].name, vars[i].loc);
30+
}
31+
return; // only analyse on instantiation
32+
}
33+
2334
if (d.isOpaque()) {
2435
ma.checkStack[ma.checkIndex-1].usedPublic = false;
2536
ma.usedPublic = false;
@@ -198,10 +209,11 @@ fn void Analyser.analyseStructNames(Analyser* ma, StructTypeDecl* d, NameVector*
198209
if (names.find(name_idx, &old_index)) {
199210
ma.error(member.getLoc(), "duplicate struct/union member '%s'", member.getName());
200211
ma.note(locs.get(old_index), "previous declaration is here");
201-
return;
212+
//return;
213+
} else {
214+
names.add(name_idx);
215+
locs.add(member.getLoc());
202216
}
203-
names.add(name_idx);
204-
locs.add(member.getLoc());
205217

206218
if (member.isStructType()) {
207219
NameVector sub_names.init(sub.getNumMembers());

0 commit comments

Comments
 (0)