Skip to content

Commit 8960a57

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 1c1a38a commit 8960a57

Some content is hidden

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

53 files changed

+1831
-439
lines changed

analyser/conversion_checker_expr.c2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ fn ExprWidth getExprWidth(const Expr* e) {
6565
return getTypeWidth(e.getType());
6666
case Type:
6767
break;
68+
case Template:
6869
case Call:
6970
return getTypeWidth(e.getType());
7071
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) {
@@ -661,12 +679,18 @@ fn void Analyser.opaque_callback(void* arg, SrcLoc loc, Decl* d) {
661679
ma.error(loc," using opaque type '%s'", qt.diagName());
662680
}
663681

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

669-
FunctionDecl* instance = ma.mod.findInstance(fd, templateType);
689+
// TODO create instiator, analyse/evaluate template expressions,
690+
// initialize TemplateSubst array from values or qualtypes,
691+
// compute instance name and check if name exists already
692+
693+
FunctionDecl* instance = (FunctionDecl*)ma.mod.findInstance(fd.asDecl(), templateType);
670694
if (!instance) {
671695
// note: template_arg decl is set here
672696
bool used_opaque = false;
@@ -676,11 +700,14 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca
676700
Decl* d = (Decl*)std;
677701
used_opaque = (std.isOpaque() && d.getModule() != ma.mod);
678702
}
703+
// TODO: use TemplateSpec in Instantiator
704+
const TemplateSpec* spec = fd.getTemplateSpec();
679705
Instantiator inst = {
680706
.c = ma.context,
681707
.ref = template_arg,
682-
.template_name = fd.getTemplateNameIdx(),
683-
.instance_ast_idx = call.getInstanceASTIdx(),
708+
.template_vars = spec.getTemplateVars(),
709+
.template_len = spec.getTemplateLen(),
710+
.instance_ast_idx = instance_ast_idx,
684711
.used_opaque = used_opaque,
685712
.arg = ma,
686713
.on_error = Analyser.opaque_callback,
@@ -691,6 +718,13 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca
691718
if (ma.has_error) return nil;
692719
d.setChecked();
693720

721+
// add instance to avoid recursive instantiation for the same type
722+
u16 instance_idx = ma.mod.addInstance(fd.asDecl(), templateType, instance.asDecl());
723+
char[64] name;
724+
// TODO: use a more readable name, eg: max$i32
725+
create_template_name(name, elemsof(name), fd.asDecl().getName(), instance_idx);
726+
d.setNameIdx(ma.astPool.addStr(name, true));
727+
694728
// Note: we need a separate scope for the body
695729
Module* template_mod = fd.asDecl().getModule();
696730
Analyser* analyser = ma.clone();
@@ -706,15 +740,7 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca
706740
analyser.free();
707741

708742
if (ma.has_error) return nil;
709-
710-
u16 instance_idx = ma.mod.addInstance(fd, templateType, instance);
711-
instance.setTemplateInstanceIdx(instance_idx);
712-
char[64] name;
713-
create_template_name(name, elemsof(name), d.getName(), instance_idx);
714-
instance.setInstanceName(ma.astPool.addStr(name, true));
715743
}
716-
call.setTemplateIdx(instance.getTemplateInstanceIdx());
717-
718744
return instance;
719745
}
720746

analyser/module_analyser_expr.c2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ fn QualType Analyser.analyseExprInner(Analyser* ma, Expr** e_ptr, u32 side) {
8787
return d.getType();
8888
case Type:
8989
break;
90+
case Template:
91+
return ma.analyseTemplateExpr(e_ptr);
9092
case Call:
9193
return ma.analyseCallExpr(e_ptr);
9294
case InitList:

analyser/module_analyser_function.c2

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,19 @@ import scope;
2121

2222
// Note: only analyses prototype + args, not the function body
2323
fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
24+
// TODO: should prevent multiple analysis
25+
//if (fd.asDecl().isChecked()) return;
26+
2427
if (fd.isTemplate()) {
25-
// do analyze template name (eg X) for name clash
26-
ma.scope.checkGlobalSymbol(fd.getTemplateNameIdx(), fd.getTemplateLoc());
28+
// do analyze template names (eg X) for name clash
29+
const TemplateSpec* spec = fd.getTemplateSpec();
30+
if (spec) {
31+
u32 len = spec.getTemplateLen();
32+
const TemplateVar* vars = spec.getTemplateVars();
33+
for (u32 i = 0; i < len; i++) {
34+
ma.scope.checkGlobalSymbol(vars[i].name, vars[i].loc);
35+
}
36+
}
2737

2838
// check rtype for array-type
2939
TypeRef* rtype = fd.getReturnTypeRef();
@@ -70,6 +80,8 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
7080
u32 first_auto_arg = 0;
7181
for (u32 i=0; i<num_params; i++) {
7282
VarDecl* vd = params[i];
83+
if (vd.asDecl().isChecked()) continue;
84+
7385
TypeRef* ref = vd.getTypeRef();
7486

7587
QualType res = ma.analyseTypeRef(ref);
@@ -128,16 +140,15 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
128140
bool is_typefn = false;
129141
if (num_params && fd.hasPrefix()) {
130142
// 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()));
143+
// Note: use TypeRef, since it's simpler to check
144+
const Ref* func_prefix = fd.getPrefix();
145+
const TypeRef* arg_ref = params[0].getTypeRef();
146+
const Ref* user = arg_ref.getUser();
147+
bool is_non_static = (user &&
148+
user.name_idx == func_prefix.name_idx &&
149+
arg_ref.getNumPointers() <= 1 &&
150+
!arg_ref.hasPrefix() &&
151+
!arg_ref.isTemplate());
141152
if (is_non_static) {
142153
fd.setCallKind(TypeFunc);
143154
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: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ import size_analyser;
2121
import value_type local;
2222

2323
fn void Analyser.analyseStructType(Analyser* ma, StructTypeDecl* d) {
24+
if (d.isTemplate()) {
25+
// do analyze template names (eg X) for name clash
26+
const TemplateSpec* spec = d.getTemplateSpec();
27+
u32 len = spec.getTemplateLen();
28+
const TemplateVar* vars = spec.getTemplateVars();
29+
for (u32 i = 0; i < len; i++) {
30+
ma.scope.checkGlobalSymbol(vars[i].name, vars[i].loc);
31+
}
32+
return; // only analyse on instantiation
33+
}
34+
2435
if (d.isOpaque()) {
2536
ma.checkStack[ma.checkIndex-1].usedPublic = false;
2637
ma.usedPublic = false;
@@ -195,10 +206,10 @@ fn void Analyser.analyseStructNames(Analyser* ma, StructTypeDecl* d, NameMap* na
195206
if (names.find(name_idx, &old_index)) {
196207
ma.error(member.getLoc(), "duplicate struct/union member '%s'", member.getName());
197208
ma.note(names.getLoc(old_index), "previous declaration is here");
198-
return;
209+
//return;
210+
} else {
211+
names.add(name_idx, member.getLoc());
199212
}
200-
names.add(name_idx, member.getLoc());
201-
202213
if (member.isStructType()) {
203214
NameMap sub_names.init(sub.getNumMembers());
204215
ma.analyseStructNames(sub, &sub_names);

0 commit comments

Comments
 (0)