From 7da4ac17c732d49f0b2e4147d79ce07de872b8af Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Wed, 14 Jan 2026 02:25:32 +0100 Subject: [PATCH] language: improve template handling * accept alternate template syntax: template definition, template(T, N=0) definition * support template block definitions: template { 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 --- analyser/conversion_checker_expr.c2 | 1 + analyser/module_analyser_call.c2 | 74 +++-- analyser/module_analyser_expr.c2 | 2 + analyser/module_analyser_function.c2 | 30 +- analyser/module_analyser_member.c2 | 1 + analyser/module_analyser_struct.c2 | 19 +- analyser/module_analyser_type.c2 | 309 +++++++++++++++++- analyser/module_analyser_unaryop.c2 | 1 + analyser/scope.c2 | 22 +- ast/ast.c2 | 73 ++--- ast/ast_evaluator.c2 | 8 +- ast/call_expr.c2 | 70 +--- ast/decl.c2 | 51 ++- ast/explicit_cast_expr.c2 | 3 +- ast/expr.c2 | 16 + ast/function_decl.c2 | 110 ++----- ast/function_decl_list.c2 | 4 + ast/identifier_expr.c2 | 13 +- ast/import_decl_list.c2 | 6 +- ast/instance_table.c2 | 43 ++- ast/instantiator.c2 | 17 +- ast/member_expr.c2 | 12 +- ast/module.c2 | 16 +- ast/statistics.c2 | 10 +- ast/stmt.c2 | 1 + ast/struct_type_decl.c2 | 101 +++++- ast/symbol_table.c2 | 9 +- ast/template_expr.c2 | 133 ++++++++ ast/template_spec.c2 | 82 +++++ ast/type_expr.c2 | 22 +- ast/type_ref.c2 | 161 +++++++-- ast/utils.c2 | 6 +- ast/value.c2 | 4 +- ast/var_decl.c2 | 8 +- ast_utils/color.c2 | 12 +- ast_utils/string_buffer.c2 | 13 + common/ast_builder.c2 | 82 ++--- generator/ast_visitor.c2 | 1 + generator/ast_visitor_expr.c2 | 9 + generator/c/c_generator.c2 | 3 + generator/c/c_generator_call.c2 | 24 +- generator/c/c_generator_expr.c2 | 9 + generator/c/c_generator_special.c2 | 9 +- generator/c/dep_finder.c2 | 6 + generator/c2i/c2i_generator_expr.c2 | 28 +- generator/ir/ir_generator.c2 | 1 + generator/ir/ir_generator_expr.c2 | 2 + ir/print.c2 | 2 +- parser/c2_parser.c2 | 134 ++++++-- parser/c2_parser_expr.c2 | 92 +++--- parser/c2_parser_stmt.c2 | 7 + parser/c2_parser_type.c2 | 6 +- plugins/unit_test_plugin.c2 | 12 +- recipe.txt | 2 + .../template/function/unknown_template_arg.c2 | 1 + test/template/samples/max.c2 | 36 ++ test/template/samples/vector.c2 | 112 +++++++ test/template/samples/vector2.c2 | 108 ++++++ test/template/samples/vector3.c2 | 130 ++++++++ test/template/samples/vector4.c2 | 126 +++++++ 60 files changed, 1903 insertions(+), 502 deletions(-) create mode 100644 ast/template_expr.c2 create mode 100644 ast/template_spec.c2 create mode 100644 test/template/samples/max.c2 create mode 100644 test/template/samples/vector.c2 create mode 100644 test/template/samples/vector2.c2 create mode 100644 test/template/samples/vector3.c2 create mode 100644 test/template/samples/vector4.c2 diff --git a/analyser/conversion_checker_expr.c2 b/analyser/conversion_checker_expr.c2 index ee9fa778b..4c3489bd8 100644 --- a/analyser/conversion_checker_expr.c2 +++ b/analyser/conversion_checker_expr.c2 @@ -64,6 +64,7 @@ fn ExprWidth getExprWidth(const Expr* e) { return getTypeWidth(e.getType()); case Type: break; + case Template: case Call: return getTypeWidth(e.getType()); case InitList: diff --git a/analyser/module_analyser_call.c2 b/analyser/module_analyser_call.c2 index 0d96d76e2..a99731c96 100644 --- a/analyser/module_analyser_call.c2 +++ b/analyser/module_analyser_call.c2 @@ -29,6 +29,31 @@ const char[] DiagTooManyArgs = "too many arguments to %sfunction call, expected const char[] DiagTooFewArgs = "too few arguments to %sfunction call, expected %d, have %d"; const char[] NoteDeclaredHere = "'%s' is defined here"; +fn QualType Analyser.analyseTemplateExpr(Analyser* ma, Expr** e_ptr) { + Expr* e = *e_ptr; + TemplateExpr* tp = (TemplateExpr*)e; + Expr** func = tp.getFunc2(); + Expr* origFn = tp.getFunc(); // store here to avoid the likely inserted FunctionPointerDecay cast + QualType qt = ma.analyseExpr(func, true, RHS); + if (qt.isInvalid()) return QualType_Invalid; + FunctionType* ft = qt.getFunctionTypeOrNil(); + if (!ft) { + ma.errorRange(origFn.getLoc(), origFn.getRange(), "object type %s is not a function template", qt.diagName()); + return QualType_Invalid; + } + FunctionDecl* fd = ft.getDecl(); + if (!fd.isTemplate() || fd.isTemplateTypeFunction()) { + ma.errorRange(e.getLoc(), e.getRange(), "function %s is not a template function", fd.asDecl().getFullName()); + return QualType_Invalid; + } + + FunctionDecl* fd2 = ma.instantiateFunctionTemplate(fd, tp.getInstanceASTIdx(), tp.getArgs(), tp.getNumArgs()); + if (!fd2) return QualType_Invalid; + fd2.asDecl().setUsed(); + tp.setDecl(fd2.asDecl()); + return fd2.asDecl().getType(); +} + fn QualType Analyser.analyseCallExpr(Analyser* ma, Expr** e_ptr) { Expr* e = *e_ptr; CallExpr* call = (CallExpr*)e; @@ -60,17 +85,10 @@ fn QualType Analyser.analyseCallExpr(Analyser* ma, Expr** e_ptr) { if (fd.hasAttrNoReturn()) call.setNoreturn(); if (fd.isTemplate()) { - if (!call.getTemplateArg()) { - ma.errorRange(e.getLoc(), e.getRange(), "function %s requires a template argument", fd.asDecl().getFullName()); - return QualType_Invalid; - } - fd = ma.instantiateTemplateFunction(call, fd); - if (!fd) return QualType_Invalid; - } else { - if (call.getTemplateArg()) { - ma.errorRange(e.getLoc(), e.getRange(), "function %s is not a template function", fd.asDecl().getFullName()); - return QualType_Invalid; - } + // TODO handle default type arguments + // TODO auto-instantiate based on actual argument types + ma.errorRange(e.getLoc(), e.getRange(), "function %s requires a template argument", fd.asDecl().getFullName()); + return QualType_Invalid; } if (fd.hasAttrDeprecated() && !ma.warnings.no_deprecated) { @@ -624,12 +642,18 @@ fn void Analyser.opaque_callback(void* arg, SrcLoc loc, Decl* d) { ma.error(loc," using opaque type '%s'", qt.diagName()); } -fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* call, FunctionDecl* fd) { - TypeRef* template_arg = call.getTemplateArg(); +fn FunctionDecl* Analyser.instantiateFunctionTemplate(Analyser* ma, FunctionDecl* fd, u16 instance_ast_idx, Expr** args, u32 num_args) { + // Only handle first argument for now, convert to type + TypeRef* template_arg = ma.getTemplateArg(*args); + if (!template_arg) return nil; QualType templateType = ma.analyseTypeRef(template_arg); if (templateType.isInvalid()) return nil; - FunctionDecl* instance = ma.mod.findInstance(fd, templateType); + // TODO create instiator, analyse/evaluate template expressions, + // initialize TemplateSubst array from values or qualtypes, + // compute instance name and check if name exists already + + FunctionDecl* instance = (FunctionDecl*)ma.mod.findInstance(fd.asDecl(), templateType); if (!instance) { // note: template_arg decl is set here bool used_opaque = false; @@ -639,11 +663,14 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca Decl* d = (Decl*)std; used_opaque = (std.isOpaque() && d.getModule() != ma.mod); } + // TODO: use TemplateSpec in Instantiator + const TemplateSpec* spec = fd.getTemplateSpec(); Instantiator inst = { .c = ma.context, .ref = template_arg, - .template_name = fd.getTemplateNameIdx(), - .instance_ast_idx = call.getInstanceASTIdx(), + .template_vars = spec.getTemplateVars(), + .template_len = spec.getTemplateLen(), + .instance_ast_idx = instance_ast_idx, .used_opaque = used_opaque, .arg = ma, .on_error = Analyser.opaque_callback, @@ -654,6 +681,13 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca if (ma.has_error) return nil; d.setChecked(); + // add instance to avoid recursive instantiation for the same type + u16 instance_idx = ma.mod.addInstance(fd.asDecl(), templateType, instance.asDecl()); + char[64] name; + // TODO: use a more readable name, eg: max$i32 + create_template_name(name, fd.asDecl().getName(), instance_idx); + d.setNameIdx(ma.astPool.addStr(name, true)); + // Note: we need a separate scope for the body Module* template_mod = fd.asDecl().getModule(); Analyser* analyser = create(ma.diags, ma.context, ma.astPool, ma.builder, ma.allmodules, ma.warnings); @@ -669,15 +703,7 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca analyser.free(); if (ma.has_error) return nil; - - u16 instance_idx = ma.mod.addInstance(fd, templateType, instance); - instance.setTemplateInstanceIdx(instance_idx); - char[64] name; - create_template_name(name, d.getName(), instance_idx); - instance.setInstanceName(ma.astPool.addStr(name, true)); } - call.setTemplateIdx(instance.getTemplateInstanceIdx()); - return instance; } diff --git a/analyser/module_analyser_expr.c2 b/analyser/module_analyser_expr.c2 index 1a76f7b4b..7edea1573 100644 --- a/analyser/module_analyser_expr.c2 +++ b/analyser/module_analyser_expr.c2 @@ -67,6 +67,8 @@ fn QualType Analyser.analyseExprInner(Analyser* ma, Expr** e_ptr, u32 side) { return d.getType(); case Type: break; + case Template: + return ma.analyseTemplateExpr(e_ptr); case Call: return ma.analyseCallExpr(e_ptr); case InitList: diff --git a/analyser/module_analyser_function.c2 b/analyser/module_analyser_function.c2 index 57ef57214..a71a330fc 100644 --- a/analyser/module_analyser_function.c2 +++ b/analyser/module_analyser_function.c2 @@ -22,8 +22,15 @@ import scope; // Note: only analyses prototype + args, not the function body fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) { if (fd.isTemplate()) { - // do analyze template name (eg X) for name clash - ma.scope.checkGlobalSymbol(fd.getTemplateNameIdx(), fd.getTemplateLoc()); + // do analyze template names (eg X) for name clash + const TemplateSpec* spec = fd.getTemplateSpec(); + if (spec) { + u32 len = spec.getTemplateLen(); + const TemplateVar* vars = spec.getTemplateVars(); + for (u32 i = 0; i < len; i++) { + ma.scope.checkGlobalSymbol(vars[i].name, vars[i].loc); + } + } // check rtype for array-type TypeRef* rtype = fd.getReturnTypeRef(); @@ -128,16 +135,15 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) { bool is_typefn = false; if (num_params && fd.hasPrefix()) { // check if SF if first arg is (const) Struct* or (const) Struct - // Note: use TypeRef, since it's faster to check - const Ref* prefix = fd.getPrefix(); - const Decl* pd = prefix.decl; - assert(pd); - QualType prefixType = pd.getType(); - - TypeRef* ref = params[0].getTypeRef(); - const Ref* param_ref = ref.getUser(); - // Note: for enum types it can be the Type or a pointer to that type - bool is_non_static = ((param_ref && param_ref.decl == prefix.decl) || ref.isPointerTo(prefixType.getIndex())); + // Note: use TypeRef, since it's simpler to check + const Ref* func_prefix = fd.getPrefix(); + const TypeRef* arg_ref = params[0].getTypeRef(); + const Ref* user = arg_ref.getUser(); + bool is_non_static = (user && + user.name_idx == func_prefix.name_idx && + arg_ref.getNumPointers() <= 1 && + !arg_ref.hasPrefix() && + !arg_ref.isTemplate()); if (is_non_static) { fd.setCallKind(CallKind.TypeFunc); is_typefn = true; diff --git a/analyser/module_analyser_member.c2 b/analyser/module_analyser_member.c2 index 9f4f777eb..3a5059b4e 100644 --- a/analyser/module_analyser_member.c2 +++ b/analyser/module_analyser_member.c2 @@ -120,6 +120,7 @@ fn QualType Analyser.analyseMemberExpr(Analyser* ma, Expr** e_ptr, u32 side) { EnumTypeDecl* etd = et.getDecl(); // check if constant/enum-function (by first casing) + // TODO: incorrect for Enum.Const.min const char* last = m.getLastMemberName(); if (ctype.isupper(last[0]) || name_idx == ma.min_idx || name_idx == ma.max_idx) { // search for constants if (valtype != ValType.NValue) { // variable of Enum type (eg Enum a; a.x) diff --git a/analyser/module_analyser_struct.c2 b/analyser/module_analyser_struct.c2 index 283270e89..a14755bc1 100644 --- a/analyser/module_analyser_struct.c2 +++ b/analyser/module_analyser_struct.c2 @@ -20,6 +20,17 @@ import name_vector local; import size_analyser; fn void Analyser.analyseStructType(Analyser* ma, StructTypeDecl* d) { + if (d.isTemplate()) { + // do analyze template names (eg X) for name clash + const TemplateSpec* spec = d.getTemplateSpec(); + u32 len = spec.getTemplateLen(); + const TemplateVar* vars = spec.getTemplateVars(); + for (u32 i = 0; i < len; i++) { + ma.scope.checkGlobalSymbol(vars[i].name, vars[i].loc); + } + return; // only analyse on instantiation + } + if (d.isOpaque()) { ma.checkStack[ma.checkIndex-1].usedPublic = false; ma.usedPublic = false; @@ -194,10 +205,11 @@ fn void Analyser.analyseStructNames(Analyser* ma, StructTypeDecl* d, NameVector* if (names.find(name_idx, &old_index)) { ma.error(member.getLoc(), "duplicate struct/union member '%s'", member.getName()); ma.note(locs.get(old_index), "previous declaration is here"); - return; + //return; + } else { + names.add(name_idx); + locs.add(member.getLoc()); } - names.add(name_idx); - locs.add(member.getLoc()); if (member.isStructType()) { NameVector sub_names.init(sub.getNumMembers()); @@ -210,4 +222,3 @@ fn void Analyser.analyseStructNames(Analyser* ma, StructTypeDecl* d, NameVector* } } - diff --git a/analyser/module_analyser_type.c2 b/analyser/module_analyser_type.c2 index 78f56d345..699306e51 100644 --- a/analyser/module_analyser_type.c2 +++ b/analyser/module_analyser_type.c2 @@ -18,6 +18,7 @@ module module_analyser; import ast local; import ctv_analyser; import src_loc local; +import string_buffer; fn void Analyser.analyseFunctionType(Analyser* ma, Decl* d) { FunctionTypeDecl* ftd = (FunctionTypeDecl*)d; @@ -160,7 +161,7 @@ fn QualType Analyser.analyseUserTypeRef(Analyser* ma, TypeRef* ref) { return QualType_Invalid; } - ref.setUser(d); + //ref.setUser(d); if (!d.isTypeDecl()) { ma.error(user.loc, "'%s' is not a type", ref.diagName()); @@ -170,6 +171,20 @@ fn QualType Analyser.analyseUserTypeRef(Analyser* ma, TypeRef* ref) { //TODO already checked in findSymbolInModule //if (!ma.scope.checkAccess(d, user.loc)) return QualType_Invalid; + if (ref.isTemplate()) { + // instantiate template type + if (!d.isStructType()) { + ma.error(ref.getLoc(), "template %s is not a struct or union type", ref.diagName()); + return QualType_Invalid; + } + StructTypeDecl* sd = (StructTypeDecl*)d; + // TODO should use instantiation AST instead of template definition AST + StructTypeDecl* sd2 = ma.instantiateStructTemplate(sd, d.getASTIdx(), ref.getArgs(), ref.getNumArgs()); + if (!sd2) return QualType_Invalid; + //tp.setDecl(fd2.asDecl()); + d = sd2.asDecl(); + } + bool full = !ref.isPointer(); DeclCheckState state = d.getCheckState(); @@ -178,6 +193,8 @@ fn QualType Analyser.analyseUserTypeRef(Analyser* ma, TypeRef* ref) { return QualType_Invalid; } + ref.setUser(d); + if (full && state != DeclCheckState.Checked) { ma.analyseGlobalDecl(d); } @@ -202,6 +219,7 @@ fn QualType Analyser.analyseTypeRef(Analyser* ma, TypeRef* ref) { assert(base.isValid()); break; case User: + // Performs template instantiation base = ma.analyseUserTypeRef(ref); if (base.isInvalid()) return base; if (!base.hasCanonicalType()) return QualType_Invalid; @@ -357,3 +375,292 @@ fn bool Analyser.checkOpaque(Analyser* ma, const StructTypeDecl* std, SrcLoc loc return true; } +#if 0 +fn TypeRef* Analyser.evalType(Analyser* ma, Expr* e) { + if (e.isType()) { + return ((TypeExpr*)e).getTypeRef(); + } + // Check if expr is a type + Value v = evalExpr(e); + if (!v.isError()) return nil; + + if (e.getKind() == Identifier) { + TypeRefHolder ref.init(); + IdentifierExpr* i = (IdentifierExpr*)e; + ref.setUser(arg.getLoc(), i.getNameIdx()); + TypeExpr* te = TypeExpr.create(ma.context, e.getLoc(), 0, &ref); + return ((TypeExpr*)te).getTypeRef(); + } + if (e.getKind() == Member && !((MemberExpr*)e).hasExpr() && ((MemberExpr*)e).getNumRefs() == 2) { + TypeRefHolder ref.init(); + MemberExpr* m = (MemberExpr*)e; + ref.setUser(m.getLoc(0), m.getNameIdx(0)); + ref.setPrefix(m.getLoc(1), m.getNameIdx(1)); + TypeExpr* te = TypeExpr.create(ma.context, e.getLoc(), 0, &ref); + return ((TypeExpr*)te).getTypeRef(); + } + return nil; +} +#endif + +fn TypeRef* Analyser.getTemplateArg(Analyser* ma, Expr* arg) { + if (arg.getKind() == Identifier) { + TypeRefHolder ref.init(); + IdentifierExpr* i = (IdentifierExpr*)arg; + ref.setUser(arg.getLoc(), i.getNameIdx()); + arg = (Expr*)TypeExpr.create(ma.context, arg.getLoc(), 0, &ref); + } else + if (arg.getKind() == Member && !((MemberExpr*)arg).hasExpr() && ((MemberExpr*)arg).getNumRefs() == 2) { + TypeRefHolder ref.init(); + MemberExpr* m = (MemberExpr*)arg; + ref.setUser(m.getLoc(0), m.getNameIdx(0)); + ref.setPrefix(m.getLoc(1), m.getNameIdx(1)); + arg = (Expr*)TypeExpr.create(ma.context, arg.getLoc(), 0, &ref); + } + + if (arg.isType()) return ((TypeExpr*)arg).getTypeRef(); + + // argument is not Identifier or MemberExpr + ma.errorRange(arg.getLoc(), arg.getRange(), "template argument is not a type"); + return nil; +} + +fn void Analyser.mangleTypeRef(Analyser* ma, const TypeRef* r, string_buffer.Buf* out, bool print_prefix) { + out.add1('$'); + if (r.isConst()) out.add("c"); + if (r.isVolatile()) out.add("v"); + + switch (r.getKind()) { + case Builtin: + out.add(r.getBuiltinKind().str()); + break; + case Void: + out.add("void"); + break; + case User: + const Decl* d = r.getUserDecl(); + assert(d); + //if (r.getNumArgs()) out.add1('I'); + if (print_prefix) { + out.addMangledName(d.getFullName(), false); + } else { + out.addMangledName(d.getName(), false); + } + if (u32 num_args = r.getNumArgs()) { + for (u32 i = 0; i < num_args; i++) { + Expr* e = r.getArg(i); + if (e.isType()) { + TypeRef* type_ref = ((TypeExpr*)e).getTypeRef(); + ma.mangleTypeRef(type_ref, out, print_prefix); + } else { + Value v = evalExpr(e); + if (!v.isError()) { + out.addMangledName(v.str(), true); + } else + if (e.getKind() == Identifier) { + TypeRefHolder ref.init(); + IdentifierExpr* ie = (IdentifierExpr*)e; + out.addMangledName(ie.getName(), true); + } else + if (e.getKind() == Member && !((MemberExpr*)e).hasExpr() && ((MemberExpr*)e).getNumRefs() == 2) { + TypeRefHolder ref.init(); + MemberExpr* m = (MemberExpr*)e; + out.addMangledName(m.getName(0), true); + out.addMangledName(m.getName(1), true); + } else { + // report error? + } + } + } + //out.add1('E'); + } + break; + case Function: + out.add("FN TODO"); + // TODO + break; + } + + for (u32 i = 0; i < r.getNumPointers(); i++) out.add1('P'); + + if (r.isIncrArray()) out.add("ai"); + + for (u32 i = 0; i < r.getNumArrays(); i++) { + const Expr* a = r.getArray(i); + // note: a can be nil, when[] + if (a) { + Value v = evalExpr(a); + out.addMangledName(v.str(), true); + } + } +} + +fn u32 Analyser.makeInstanceMangledName(Analyser* ma, Decl* d, Expr** args, u32 num_args, bool full) { + const char* template_name = d.getName(); + if (!template_name) return 0; + string_buffer.Buf* out = string_buffer.create(128, false, 0); + if (full) out.print("%s_", ma.mod.getName()); + //out.add("_I"); + out.addMangledName(template_name, false); + for (u32 i = 0; i < num_args; i++) { + Expr* e = args[i]; + if (e.isType()) { + TypeRef* type_ref = ((TypeExpr*)e).getTypeRef(); + //type_ref.mangle(out, false); + ma.mangleTypeRef(type_ref, out, false); + } else { + Value v = evalExpr(e); + if (!v.isError()) { + out.addMangledName(v.str(), true); + } else + if (e.getKind() == Identifier) { + TypeRefHolder ref.init(); + IdentifierExpr* ie = (IdentifierExpr*)e; + out.addMangledName(ie.getName(), true); + } else + if (e.getKind() == Member && !((MemberExpr*)e).hasExpr() && ((MemberExpr*)e).getNumRefs() == 2) { + TypeRefHolder ref.init(); + MemberExpr* m = (MemberExpr*)e; + out.addMangledName(m.getName(0), true); + out.addMangledName(m.getName(1), true); + } else { + ma.error(e.getLoc(), "invalid template argument"); + } + } + } + u32 name_idx = ma.astPool.addStr(out.data(), true); + out.free(); + return name_idx; +} + +fn u32 Analyser.makeInstanceTypeName(Analyser* ma, Decl* d, Expr** args, u32 num_args) { + const char* template_name = d.getName(); + string_buffer.Buf* out = string_buffer.create(128, false, 0); + out.print("%s<", template_name); + for (u32 i = 0; i < num_args; i++) { + if (i > 0) out.add1(','); + Expr* e = args[i]; + if (e.isType()) { + TypeRef* type_ref = ((TypeExpr*)e).getTypeRef(); + type_ref.printLiteral(out, false); + } else { + Value v = evalExpr(e); + if (!v.isError()) { + out.add(v.str()); + } else + if (e.getKind() == Identifier) { + TypeRefHolder ref.init(); + IdentifierExpr* ie = (IdentifierExpr*)e; + out.add(ie.getName()); + } else + if (e.getKind() == Member && !((MemberExpr*)e).hasExpr() && ((MemberExpr*)e).getNumRefs() == 2) { + TypeRefHolder ref.init(); + MemberExpr* m = (MemberExpr*)e; + out.add(m.getName(0)); + out.add1('.'); + out.add(m.getName(1)); + } else { + ma.error(e.getLoc(), "invalid template argument"); + return 0; + } + } + } + out.add1('>'); + u32 name_idx = ma.astPool.addStr(out.data(), true); + out.free(); + return name_idx; +} + +fn StructTypeDecl* Analyser.instantiateStructTemplate(Analyser* ma, StructTypeDecl* std, u16 instance_ast_idx, Expr** args, u32 num_args) { + // Only handle first argument for now, convert to type + TypeRef* template_arg = ma.getTemplateArg(*args); + if (!template_arg) return nil; + QualType templateType = ma.analyseTypeRef(template_arg); + if (templateType.isInvalid()) return nil; + + u32 instance_name_idx = ma.makeInstanceTypeName(std.asDecl(), args, num_args); + if (!instance_name_idx) return nil; + u32 instance_cname_idx = ma.makeInstanceMangledName(std.asDecl(), args, num_args, true); + if (!instance_cname_idx) return nil; + instance_name_idx = ma.makeInstanceMangledName(std.asDecl(), args, num_args, false); + if (!instance_name_idx) return nil; + + StructTypeDecl* instance = (StructTypeDecl*)ma.mod.findInstance(std.asDecl(), templateType); + if (!instance) { + // note: template_arg decl is set here + bool used_opaque = false; + StructType* st = templateType.getStructTypeOrNil(); + if (st) { + StructTypeDecl* std2 = st.getDecl(); + Decl* d2 = (Decl*)std2; + used_opaque = (std2.isOpaque() && d2.getModule() != ma.mod); + } + // TODO: use TemplateSpec in Instantiator + const TemplateSpec* spec = std.getTemplateSpec(); + Instantiator inst = { + .c = ma.context, + .ref = template_arg, + .template_vars = spec.getTemplateVars(), + .template_len = spec.getTemplateLen(), + .instance_ast_idx = instance_ast_idx, + .used_opaque = used_opaque, + .arg = ma, + .on_error = Analyser.opaque_callback, + } + + instance = std.instantiate(&inst); + Decl* d = (Decl*)instance; + + u16 instance_idx = ma.mod.addInstance(std.asDecl(), templateType, instance.asDecl()); +#if 0 + char[64] instance_name; + // TODO: use a more readable name, eg: max$i32 + create_template_name(instance_name, std.asDecl().getName(), instance_idx); + u32 instance_name_idx = ma.astPool.addStr(instance_name, true); +#endif + d.setCName(instance_cname_idx); + d.setNameIdx(instance_name_idx); + + // TODO check for duplicate name + // TODO use which AST and module? + ma.mod.addTypeDecl(d, instance_ast_idx); + ma.mod.addSymbol(instance_name_idx, d); + + ma.analyseGlobalDecl(d); + if (ma.has_error) return nil; + + d.setChecked(); + + // Add the struct type itself as a template var + inst.template_type_name = std.asDecl().getNameIdx(); + inst.template_type_ref.setUser(d.getLoc(), instance_name_idx); + instance.instantiateTypeFunctions(&inst, std); + u32 num_sf = instance.getNumStructFunctions(); + for (u32 i = 0; i < num_sf; i++) { + FunctionDecl* fd = instance.getStructFunction(i); + ma.mod.addFunc(fd, instance_ast_idx); + ma.analyseFunction(fd); + } +#if 0 + // Template function instance body will be analysed in outer loop + // Note: we need a separate scope for the body + Module* template_mod = std.asDecl().getModule(); + Analyser* analyser = create(ma.diags, ma.context, ma.astPool, ma.builder, ma.allmodules, ma.warnings); + analyser.setMod(template_mod); + scope.Scope* tmpScope = scope.create(ma.allmodules, + ma.diags, + d.getAST().getImports(), + template_mod, + template_mod.getSymbols(), + !ma.warnings.no_unused_variable); + for (u32 i = 0; i < num_sf; i++) { + FunctionDecl* fd = instance.getStructFunction(i); + analyser.analyseFunctionBody(fd, tmpScope); + } + tmpScope.free(); + analyser.free(); +#endif + if (ma.has_error) return nil; + } + return instance; +} diff --git a/analyser/module_analyser_unaryop.c2 b/analyser/module_analyser_unaryop.c2 index 66483e7e9..d2eb65edf 100644 --- a/analyser/module_analyser_unaryop.c2 +++ b/analyser/module_analyser_unaryop.c2 @@ -221,6 +221,7 @@ fn IdentifierKind getInnerExprAddressOf(const Expr* e) { IdentifierExpr* i = (IdentifierExpr*)e; return i.getKind(); case Type: + case Template: case Call: case InitList: case FieldDesignatedInit: diff --git a/analyser/scope.c2 b/analyser/scope.c2 index 0c5c5649b..1ed429ed6 100644 --- a/analyser/scope.c2 +++ b/analyser/scope.c2 @@ -106,10 +106,8 @@ public fn void Scope.reset(Scope* s) { fn void Scope.addImports(Scope* s) { // Note: this also adds the module itself (import[0]) - u32 num_imports = s.imports.size(); - ast.ImportDecl** imports = s.imports.getDecls(); - for (u32 i=0; i 1) out.newline(); - Decl** types = a.types.getDecls(); for (u32 i=0; i Foo*) // Note: the rtype could change, so this could make the size larger or smaller: // X could be replaced by mod.Foo, then bigger // X could be replaced by i32, then smaller - - bool rtype_matches = fd.rtype.matchesTemplate(fd.template_name); - u32 extra = rtype_matches ? inst.ref.getExtraSize() : fd.rtype.getExtraSize(); + u32 extra = fd.rtype.instantiateExtraSize(inst); u32 num_params = fd.num_params; u32 size = sizeof(FunctionDecl) + num_params * sizeof(VarDecl*) + extra; FunctionDecl* fd2 = inst.c.alloc(size); // copy members fd2.base = fd.base; + fd2.base.declBits.check_state = DeclCheckState.Unchecked; // note: the instantiation is no longer a template function (otherwise ignored by analyseFunctionProto) + fd2.base.functionDeclBits.is_template_type_function = false; fd2.base.functionDeclBits.def_kind = DefKind.Global; FunctionType* ftype = FunctionType.create(inst.c, fd2); fd2.base.qt = QualType.create(ftype.asType()); @@ -209,12 +168,14 @@ public fn FunctionDecl* FunctionDecl.instantiate(const FunctionDecl* fd, Instant fd2.rt = QualType_Invalid; fd2.num_params = fd.num_params; fd2.attr_format_arg = fd.attr_format_arg; - fd2.instance_idx = 0; - fd2.template_name = 0; - fd2.template_loc = fd.template_loc; fd2.flagBits = fd.flagBits; - fd2.flags.instance_ast_idx = (u16)inst.instance_ast_idx; + fd2.flags.instance_ast_idx = inst.instance_ast_idx; + fd2.template_decl = fd; + // Perform prefix substitution fd2.prefix = fd.prefix; + if (fd.hasPrefix() && fd.prefix.name_idx == inst.template_type_name) { + fd2.prefix.name_idx = inst.template_type_ref.user.name_idx; + } fd2.rtype.instantiate(&fd.rtype, inst); VarDecl** src = fd.rtype.getPointerAfter(); @@ -308,6 +269,15 @@ public fn bool FunctionDecl.isTemplate(const FunctionDecl* d) { return d.base.functionDeclBits.def_kind == DefKind.Template; } +public fn bool FunctionDecl.isTemplateTypeFunction(const FunctionDecl* d) { + return d.base.functionDeclBits.is_template_type_function; +} + +fn void FunctionDecl.setTemplateTypeFunction(FunctionDecl* d) { + d.base.functionDeclBits.is_template_type_function = true; + d.base.functionDeclBits.def_kind = DefKind.Template; +} + public fn bool FunctionDecl.isMemberType(const FunctionDecl* d) { return d.base.functionDeclBits.def_kind == DefKind.StructMember; } @@ -320,24 +290,9 @@ fn DefKind FunctionDecl.getDefKind(const FunctionDecl* d) { return (DefKind)d.base.functionDeclBits.def_kind; } -public fn u32 FunctionDecl.getTemplateNameIdx(const FunctionDecl* d) { - return d.template_name; -} - -public fn SrcLoc FunctionDecl.getTemplateLoc(const FunctionDecl* d) { - return d.template_loc; -} - -public fn void FunctionDecl.setTemplateInstanceIdx(FunctionDecl* d, u16 idx) { - d.instance_idx = idx; -} - -public fn u16 FunctionDecl.getTemplateInstanceIdx(const FunctionDecl* d) { - return d.instance_idx; -} - -public fn void FunctionDecl.setInstanceName(FunctionDecl* d, u32 name_idx) { - d.base.name_idx = name_idx; +public fn const TemplateSpec* FunctionDecl.getTemplateSpec(const FunctionDecl* d) { + if (d.isTemplate()) return d.template_spec; + return nil; } public fn Module* FunctionDecl.getInstanceModule(FunctionDecl* d) { @@ -497,6 +452,7 @@ fn void FunctionDecl.print(const FunctionDecl* d, string_buffer.Buf* out, u32 in out.space(); out.color(col_Attr); out.add(callKind_names[d.getCallKind()]); + if (d.isTemplateTypeFunction()) out.add(" TTF"); d.base.printAttrs(out); out.add(" def:"); out.add(defKind_names[d.getDefKind()]); @@ -534,10 +490,8 @@ fn void FunctionDecl.print(const FunctionDecl* d, string_buffer.Buf* out, u32 in else out.add(""); out.newline(); - if (d.base.functionDeclBits.def_kind == DefKind.Template) { - out.indent(indent + 1); - out.color(col_Template); - out.print("template %s\n", idx2name(d.template_name)); + if (const TemplateSpec* template_spec = d.getTemplateSpec()) { + template_spec.print(out, indent); } //d.rtype.print(out, true,); diff --git a/ast/function_decl_list.c2 b/ast/function_decl_list.c2 index ba0cb16f6..c58e539b7 100644 --- a/ast/function_decl_list.c2 +++ b/ast/function_decl_list.c2 @@ -56,6 +56,10 @@ public fn u32 FunctionDeclList.size(const FunctionDeclList* l) { return l.count; } +public fn FunctionDecl* FunctionDeclList.get(const FunctionDeclList* l, u32 i) @(unused) { + return l.decls[i]; +} + public fn FunctionDecl** FunctionDeclList.getDecls(const FunctionDeclList* l) { return l.decls; } diff --git a/ast/identifier_expr.c2 b/ast/identifier_expr.c2 index 154b79098..d8047255b 100644 --- a/ast/identifier_expr.c2 +++ b/ast/identifier_expr.c2 @@ -69,7 +69,18 @@ public fn IdentifierExpr* IdentifierExpr.create(ast_context.Context* c, SrcLoc l return e; } +fn const TypeRef* IdentifierExpr.matchesTemplate(IdentifierExpr* e, Instantiator* inst) { + if (e.name_idx == inst.template_type_name) + return (TypeRef*)&inst.template_type_ref; + // TODO: support multiple arguments + if (e.name_idx == inst.template_vars[0].name) return inst.ref; + return nil; +} + fn Expr* IdentifierExpr.instantiate(IdentifierExpr* e, Instantiator* inst) { + if (const TypeRef* r2 = e.matchesTemplate(inst)) { + return (Expr*)TypeExpr.createFromTypeRef(inst.c, e.base.base.loc, (u32)strlen(e.getName()), r2); + } return (Expr*)IdentifierExpr.create(inst.c, e.base.base.loc, e.name_idx); } @@ -124,7 +135,7 @@ fn void IdentifierExpr.print(const IdentifierExpr* e, string_buffer.Buf* out, u3 out.space(); if (e.base.base.identifierExprBits.has_decl) { out.color(col_Value); - out.add(e.decl.getName()); + out.add(e.decl.getFullName()); } else { out.color(col_Value); out.add(idx2name(e.name_idx)); diff --git a/ast/import_decl_list.c2 b/ast/import_decl_list.c2 index 2d3e339cf..87292a5ee 100644 --- a/ast/import_decl_list.c2 +++ b/ast/import_decl_list.c2 @@ -52,7 +52,11 @@ public fn u32 ImportDeclList.size(const ImportDeclList* l) { return l.count; } -public fn ImportDecl** ImportDeclList.getDecls(const ImportDeclList* l) { +public fn ImportDecl* ImportDeclList.get(const ImportDeclList* l, u32 i) { + return l.decls[i]; +} + +public fn ImportDecl** ImportDeclList.getDecls(const ImportDeclList* l) @(unused) { return l.decls; } diff --git a/ast/instance_table.c2 b/ast/instance_table.c2 index 4634aaefb..abfc5bff7 100644 --- a/ast/instance_table.c2 +++ b/ast/instance_table.c2 @@ -20,17 +20,17 @@ import stdlib; type TemplateInstance struct { QualType qt; - FunctionDecl* instance; + Decl* instance; } -type TemplateFunction struct { - const FunctionDecl* fd; +type TemplateInstanceList struct { + const Decl* fd; u16 count; u16 capacity; TemplateInstance* instances; } -fn void TemplateFunction.init(TemplateFunction* f, const FunctionDecl* fd) { +fn void TemplateInstanceList.init(TemplateInstanceList* f, const Decl* fd) { f.fd = fd; f.count = 0; f.capacity = 0; @@ -38,7 +38,7 @@ fn void TemplateFunction.init(TemplateFunction* f, const FunctionDecl* fd) { f.resize(2); } -fn void TemplateFunction.resize(TemplateFunction* f, u16 capacity) { +fn void TemplateInstanceList.resize(TemplateInstanceList* f, u16 capacity) { f.capacity = capacity; TemplateInstance* inst2 = stdlib.malloc(capacity* sizeof(TemplateInstance)); if (f.count) { @@ -48,7 +48,7 @@ fn void TemplateFunction.resize(TemplateFunction* f, u16 capacity) { f.instances = inst2; } -fn u16 TemplateFunction.add(TemplateFunction* f, QualType qt, FunctionDecl* instance) { +fn u16 TemplateInstanceList.add(TemplateInstanceList* f, QualType qt, Decl* instance) { if (f.count == f.capacity) f.resize(f.capacity * 2); u16 idx = f.count; @@ -59,7 +59,7 @@ fn u16 TemplateFunction.add(TemplateFunction* f, QualType qt, FunctionDecl* inst return idx; } -fn FunctionDecl* TemplateFunction.find(const TemplateFunction* f, QualType qt) { +fn Decl* TemplateInstanceList.find(const TemplateInstanceList* f, QualType qt) { for (u32 i=0; i ref_idx) { return ref.decl.getName(); @@ -289,6 +289,10 @@ public fn const char* MemberExpr.getLastMemberName(const MemberExpr* e) { return e.getName(e.getNumRefs()-1); } +public fn u32 MemberExpr.getLastMemberNameIdx(const MemberExpr* e) @(unused) { + return e.getNameIdx(e.getNumRefs()-1); +} + fn void MemberExpr.print(const MemberExpr* e, string_buffer.Buf* out, u32 indent) { e.base.printKind(out, indent); e.base.printTypeBits(out); @@ -316,6 +320,12 @@ fn void MemberExpr.print(const MemberExpr* e, string_buffer.Buf* out, u32 indent out.print(" refs=%d/%d ", e.base.base.memberExprBits.num_decls, e.getNumRefs()); out.color(col_Value); e.printLiteral(out); + if (kind == Function || kind == Type || kind == Var) { + if (Decl* d = e.getDecl(e.getNumRefs() - 1)) { + out.space(); + out.add(d.getFullName()); + } + } out.newline(); if (e.hasExpr()) e.refs[0].expr.print(out, indent + 1); } diff --git a/ast/module.c2 b/ast/module.c2 index 51c98cd98..035446d27 100644 --- a/ast/module.c2 +++ b/ast/module.c2 @@ -210,18 +210,28 @@ public fn ast.Decl* Module.findPrivateSymbol(const Module* m, u32 name_idx) { return m.symbols.findPrivate(name_idx); } -public fn ast.FunctionDecl* Module.findInstance(const Module* m, ast.FunctionDecl* fd, QualType qt) { +public fn ast.Decl* Module.findInstance(const Module* m, ast.Decl* fd, QualType qt) { return m.instances.find(fd, qt); } -public fn u16 Module.addInstance(Module* m, ast.FunctionDecl* fd, QualType qt, ast.FunctionDecl* instance) { +public fn u16 Module.addInstance(Module* m, ast.Decl* fd, QualType qt, ast.Decl* instance) { return m.instances.add(fd, qt, instance); } -public fn ast.FunctionDecl* Module.getInstance(const Module* m, ast.FunctionDecl* fd, u32 idx) { +public fn ast.Decl* Module.getInstance(const Module* m, ast.Decl* fd, u32 idx) @(unused) { return m.instances.get(fd, idx); } +public fn void Module.addTypeDecl(Module* m, ast.Decl* d, u16 instance_ast_idx) { + AST* ast_ = idx2ast(instance_ast_idx); + ast_.addTypeDecl(d); +} + +public fn void Module.addFunc(Module* m, ast.FunctionDecl* d, u16 instance_ast_idx) { + AST* ast_ = idx2ast(instance_ast_idx); + ast_.addFunc(d); +} + public fn void Module.print(const Module* m, string_buffer.Buf* out, bool show_funcs) { out.color(color.Normal); out.print("------ module %s (used %d, exported %d, private %d) ------\n", idx2name(m.name_idx), m.is_used, m.is_exported, m.is_private); diff --git a/ast/statistics.c2 b/ast/statistics.c2 index b97a4b186..c94772ff6 100644 --- a/ast/statistics.c2 +++ b/ast/statistics.c2 @@ -32,7 +32,7 @@ type Stats struct { Stat[elemsof(ExprKind)] exprs; Stat[elemsof(StmtKind)] stmts; Stat[elemsof(DeclKind)] decls; - Stat[3] others; // 0=ArrayValue, 1=StaticAssert, 2=SwitchCase + Stat[4] others; // 0=ArrayValue, 1=StaticAssert, 2=SwitchCase, 3=TemplateSpec } fn void Stats.reset(Stats* s) { @@ -67,7 +67,7 @@ fn void Stats.addDecl(DeclKind kind, u32 size) { globals.stats.decls[kind].add(size); } -const char*[] other_names = { "ArrayValue", "StaticAssert", "SwitchCase" } +const char*[] other_names = { "ArrayValue", "StaticAssert", "SwitchCase", "TemplateSpec" } fn void Stats.addArrayValue(u32 size) { globals.stats.others[0].add(size); @@ -81,6 +81,10 @@ fn void Stats.addSwitchCase(u32 size) { globals.stats.others[2].add(size); } +fn void Stats.addTemplateSpec(u32 size) { + globals.stats.others[3].add(size); +} + fn void Stat.slack(const Stat* ss, u32* countp, u32* totalp) { if (BucketWidth & 0x7) { for (u32 cat = 0; cat < NCat; cat++) { @@ -157,7 +161,7 @@ fn void Stats.dump(const Stats* s) { printf("--- Other ---\n"); u32 otherTotal = 0; u32 otherCount = 0; - for (u32 i=0; i<3; i++) { + for (u32 i = 0; i < elemsof(s.others); i++) { s.others[i].dump(other_names[i], &otherCount, &otherTotal); } printf(" %20s %6d %7d\n", "total", otherCount, otherTotal); diff --git a/ast/stmt.c2 b/ast/stmt.c2 index 2f90f6d1b..01dd1bb24 100644 --- a/ast/stmt.c2 +++ b/ast/stmt.c2 @@ -79,6 +79,7 @@ public type Stmt struct @(opaque) { BitOffsetExprBits bitOffsetBits; BooleanLiteralBits booleanLiteralBits; BuiltinExprBits builtinExprBits; + TemplateExprBits templateExprBits; CallExprBits callExprBits; CharLiteralBits charLiteralBits; ExplicitCastExprBits explicitCastExprBits; diff --git a/ast/struct_type_decl.c2 b/ast/struct_type_decl.c2 index a379bb276..6913d6d62 100644 --- a/ast/struct_type_decl.c2 +++ b/ast/struct_type_decl.c2 @@ -98,6 +98,10 @@ public type StructTypeDecl struct @(opaque) { u32 num_struct_functions; FunctionDecl** struct_functions; FieldInitInfo* field_info; // set lazily on Field-Designated inits + union { + TemplateSpec* template_spec; + const StructTypeDecl* template_decl; + } Decl*[0] members; // tail-allocated //StructLayout layout[0]; // tail-allocated } @@ -110,7 +114,8 @@ public fn StructTypeDecl* StructTypeDecl.create(ast_context.Context* c, bool is_struct, bool is_global, Decl** members, - u32 num_members) + u32 num_members, + TemplateSpec* template_spec) { u32 size = sizeof(StructTypeDecl) + num_members * sizeof(Decl*); size += sizeof(StructLayout); @@ -129,6 +134,7 @@ public fn StructTypeDecl* StructTypeDecl.create(ast_context.Context* c, d.num_struct_functions = 0; d.struct_functions = nil; d.field_info = nil; + d.template_spec = template_spec; StructLayout* layout = d.getLayout(); layout.size = 0; @@ -145,6 +151,57 @@ public fn StructTypeDecl* StructTypeDecl.create(ast_context.Context* c, return d; } +public fn StructTypeDecl* StructTypeDecl.instantiate(const StructTypeDecl* d, Instantiator* inst) +{ + u32 num_members = d.num_members; + u32 size = sizeof(StructTypeDecl) + num_members * sizeof(Decl*); + size += sizeof(StructLayout); + size += num_members * sizeof(StructMemberLayout); // to store StructMemberLayouts + size = (size + 7) & ~0x7; // round to 8-byte (temp) + StructTypeDecl* d2 = inst.c.alloc(size); + StructType* stype = StructType.create(inst.c, d2); + QualType qt = QualType.create(stype.asType()); + stype.asType().setCanonicalType(qt); + // copy members + d2.base = d.base; + d2.base.declBits.check_state = DeclCheckState.Unchecked; + d2.base.qt = qt; + + d2.num_members = num_members; + d2.num_struct_functions = 0; + d2.struct_functions = nil; + d2.field_info = nil; + d2.template_spec = nil; + //d2.template_decl = d; + + // TODO: use init function + StructLayout* layout = d2.getLayout(); + layout.size = 0; + layout.alignment = 0; + layout.attr_alignment = 1; + string.memset(layout.members, 0, num_members * sizeof(StructMemberLayout)); + + for (u32 i = 0; i < num_members; i++) { + d2.members[i] = d.members[i].instantiate(inst); + } +#if AstStatistics + Stats.addDecl(DeclKind.StructType, size); +#endif + return d2; +} + +public fn void StructTypeDecl.instantiateTypeFunctions(StructTypeDecl* d2, Instantiator* inst, StructTypeDecl* d) +{ + u32 num_sf = d.num_struct_functions; + d2.struct_functions = inst.c.alloc(num_sf * sizeof(FunctionDecl*)); + for (u32 i = 0; i < num_sf; i++) { + FunctionDecl* fd = d.struct_functions[i].instantiate(inst); + fd.asDecl().setAttrUnused(); + d2.struct_functions[i] = fd; + } + d2.num_struct_functions = num_sf; +} + public fn Decl* StructTypeDecl.asDecl(StructTypeDecl* d) { return &d.base; } @@ -166,15 +223,22 @@ public fn bool StructTypeDecl.isUnion(const StructTypeDecl* d) { return !d.base.structTypeDeclBits.is_struct; } -fn const FunctionDecl** StructTypeDecl.getStructFunctions(const StructTypeDecl* d) { - // TEMP const-cast until ptr-ptr -> const ptr-ptr is fixed in analyser - return (const FunctionDecl**)d.struct_functions; +public fn TemplateSpec* StructTypeDecl.getTemplateSpec(const StructTypeDecl* d) { + return d.template_spec; } -fn u32 StructTypeDecl.getNumStructFunctions(const StructTypeDecl* d) { +public fn bool StructTypeDecl.isTemplate(const StructTypeDecl* d) { + return d.template_spec != nil; +} + +public fn u32 StructTypeDecl.getNumStructFunctions(const StructTypeDecl* d) { return d.num_struct_functions; } +public fn FunctionDecl* StructTypeDecl.getStructFunction(const StructTypeDecl* d, u32 i) { + return d.struct_functions[i]; +} + public fn StructLayout* StructTypeDecl.getLayout(const StructTypeDecl* d) { return (StructLayout*)&d.members[d.num_members]; } @@ -233,6 +297,11 @@ public fn void StructTypeDecl.setStructFunctions(StructTypeDecl* d, ast_context. string.memcpy(dest, funcs, size); d.struct_functions = dest; d.num_struct_functions = count; + if (d.isTemplate()) { + for (u32 i = 0; i < count; i++) { + dest[i].setTemplateTypeFunction(); + } + } } public fn Decl* StructTypeDecl.findAny(const StructTypeDecl* s, u32 name_idx) { @@ -355,17 +424,23 @@ fn void StructTypeDecl.print(const StructTypeDecl* d, string_buffer.Buf* out, u3 out.color(col_Value); if (d.base.getName()) out.add(d.base.getName()); else out.add(""); - out.newline(); + if (const TemplateSpec* template_spec = d.getTemplateSpec()) { + template_spec.print(out, indent); + } else { + out.newline(); + } for (u32 i=0; i'); +} + +fn void TemplateExpr.print(const TemplateExpr* e, string_buffer.Buf* out, u32 indent) { + e.base.printKind(out, indent); + e.base.printTypeBits(out); + out.newline(); + e.func.print(out, indent + 1); + for (u32 i=0; i 1) { + out.add("template("); + for (u32 i = 0; i < spec.len; i++) { + if (i) out.add(", "); + out.add(idx2name(spec.vars[i].name)); + } + out.add(")\n"); + } else { + out.print("template %s\n", idx2name(spec.vars[0].name)); + } +} + diff --git a/ast/type_expr.c2 b/ast/type_expr.c2 index 9b728499d..f904175a3 100644 --- a/ast/type_expr.c2 +++ b/ast/type_expr.c2 @@ -19,6 +19,8 @@ import ast_context; import string_buffer; import src_loc local; +import string local; + type TypeExprBits struct { u32 : NumExprBits; u32 src_len : 32 - NumExprBits; @@ -45,9 +47,25 @@ public fn TypeExpr* TypeExpr.create(ast_context.Context* c, return e; } +fn TypeExpr* TypeExpr.createFromTypeRef(ast_context.Context* c, + SrcLoc loc, + u32 src_len, + const TypeRef* ref) +{ + u32 extra = ref.getExtraSize(); + u32 size = sizeof(TypeExpr) + extra; + TypeExpr* e = c.alloc(size); + e.base.init(ExprKind.Type, loc, 0, 0, 0, ValType.NValue); + e.base.base.typeExprBits.src_len = src_len; + memcpy(&e.typeRef, ref, sizeof(TypeRef) + extra); +#if AstStatistics + Stats.addExpr(ExprKind.Type, size); +#endif + return e; +} + fn Expr* TypeExpr.instantiate(TypeExpr* e, Instantiator* inst) { - bool matches = e.typeRef.matchesTemplate(inst.template_name); - u32 extra = matches ? inst.ref.getExtraSize() : e.typeRef.getExtraSize(); + u32 extra = e.typeRef.instantiateExtraSize(inst); u32 size = sizeof(TypeExpr) + extra; TypeExpr* e2 = inst.c.alloc(size); e2.base = e.base; diff --git a/ast/type_ref.c2 b/ast/type_ref.c2 index af04cfcd4..c0fac9921 100644 --- a/ast/type_ref.c2 +++ b/ast/type_ref.c2 @@ -40,6 +40,7 @@ fn const char* TypeRefKind.str(TypeRefKind k) { type TypeRefBits struct { u32 is_const : 1; u32 is_volatile : 1; + u32 num_args : 3; // limit to 7 template arguments u32 num_ptrs : 2; // 0 = Foo, 1 = Foo*, 2 = Foo** u32 num_arrays : 2; // limit to 3-dimensional arrays u32 incr_array : 1; @@ -71,6 +72,7 @@ public type TypeRef struct @(opaque, aligned=8) { } Ref[0] refs; // tail-allocated. [1] if is_user/struct_member_fn, [2] if also has_prefix //Expr*[0] arrays; // tail-allocated. [1] if Foo[..], [2] if Foo[..][..], etc + //Expr*[0] args; // tail-allocated. for template arguments } // just a TypeRef with all possible tail-allocated fields @@ -79,6 +81,7 @@ public type TypeRefHolder struct { Ref user; Ref prefix; Expr*[3] arrays; // Note: in order of parsing: so [4][20] -> [4] [20] + Expr*[MaxTemplateArg] args; } public fn void TypeRefHolder.init(TypeRefHolder* h) { @@ -155,6 +158,14 @@ public fn void TypeRefHolder.addArray(TypeRefHolder* h, Expr* array) { r.flags.num_arrays++; } +public fn bool TypeRefHolder.addArg(TypeRefHolder* h, Expr* arg) { + TypeRef* r = (TypeRef*)&h.ref; + if (r.flags.num_args >= MaxTemplateArg) return false; + h.args[r.flags.num_args] = arg; + r.flags.num_args++; + return true; +} + public fn void TypeRefHolder.setBuiltin(TypeRefHolder* h, BuiltinKind kind, SrcLoc loc) { TypeRef* r = (TypeRef*)&h.ref; r.flags.builtin_kind = kind; @@ -227,42 +238,94 @@ fn void TypeRef.init(TypeRef* dest, const TypeRefHolder* h) { } for (u32 i=0; i 3) return false; + if (r1.flags.num_ptrs + r2.flags.num_ptrs > 3) return false; + if (r1.flags.num_ptrs && r2.flags.num_arrays) return false; + return true; +} + +fn u32 TypeRef.instantiateExtraSize(const TypeRef* r1, Instantiator* inst) { + if (const TypeRef* r2 = r1.matchesTemplate(inst)) { + if (r1.instantiateCheck(r2, inst)) + return r2.getExtraSize() + r1.flags.num_arrays * sizeof(Expr*); + } + return r1.getExtraSize(); } fn void TypeRef.instantiate(TypeRef* r, const TypeRef* r1, Instantiator* inst) { - const TypeRef* r2 = inst.ref; - if (r1.matchesTemplate(inst.template_name)) { + // TODO: instantiate template arguments and array dimension expressions + if (const TypeRef* r2 = r1.matchesTemplate(inst)) { // Note: keep the srcloc intact // TODO enforce no prefix in template functions r.flagBits = r2.flagBits; - r.dest = r2.dest; + r.flags.is_const |= r1.flags.is_const; + r.flags.is_volatile |= r1.flags.is_volatile; + r.flags.num_ptrs += r1.flags.num_ptrs; + r.flags.num_arrays += r1.flags.num_arrays; + r.dest = r2.dest; // ??? + u8* ptr = (u8*)&r.refs[0]; + // r1 is TypeRefKind.Builtin without prefix + const u8* ptr1 = (const u8*)&r1.refs[0] + sizeof(Ref); + const u8* ptr2 = (const u8*)&r2.refs[0]; + u32 ref_size = ((r2.flags.kind >= TypeRefKind.User) + r2.flags.has_prefix) * sizeof(Ref); + if (ref_size) string.memcpy(ptr, ptr2, ref_size); + ptr += ref_size; + ptr2 += ref_size; if (r2.flags.kind == TypeRefKind.User) { - r.refs[0].name_idx = r2.refs[0].name_idx; r.refs[0].loc = r1.refs[0].loc; // location from instance? - r.refs[0].decl = r2.refs[0].decl; - if (r2.flags.has_prefix) { - r.refs[1].name_idx = r2.refs[1].name_idx; - r.refs[1].loc = r2.refs[1].loc; - r.refs[1].decl = r2.refs[1].decl; + } + if (u32 array_size = r1.flags.num_arrays * sizeof(Expr*)) { + string.memcpy(ptr, ptr1, array_size); + ptr += array_size; + ptr1 += array_size; + // instantiate array expressions + for (u32 i = 0; i < r1.flags.num_arrays; i++) { + r.setArray(i, r.getArray(i).instantiate(inst)); } } - r.flags.is_const |= r1.flags.is_const; - r.flags.is_volatile |= r1.flags.is_volatile; - r.flags.num_ptrs += r1.flags.num_ptrs; - + if (u32 array_size = r2.flags.num_arrays * sizeof(Expr*)) { + string.memcpy(ptr, ptr2, array_size); + ptr += array_size; + ptr2 += array_size; + } + if (u32 array_size = r2.flags.num_args * sizeof(Expr*)) { + string.memcpy(ptr, ptr2, array_size); + ptr += array_size; + ptr2 += array_size; + } if (inst.used_opaque && r.flags.num_ptrs == 0) { inst.on_opaque(r.refs[0].loc, r.refs[0].decl); } } else { string.memcpy(r, r1, sizeof(TypeRef) + r1.getExtraSize()); + // instantiate template arguments + for (u32 i = 0; i < r1.flags.num_args; i++) { + r.setArg(i, r1.getArg(i).instantiate(inst)); + } } } @@ -273,9 +336,9 @@ public fn void TypeRef.setDest(TypeRef* r, u32 dest) { fn u32 TypeRef.getExtraSize(const TypeRef* r) { // User, StructMemberType use Ref - u32 numrefs = (r.flags.kind != TypeRefKind.Builtin) + r.flags.has_prefix; + u32 numrefs = (r.flags.kind >= TypeRefKind.User) + r.flags.has_prefix; u32 extra = numrefs * sizeof(Ref); - extra += r.flags.num_arrays * sizeof(Expr*); + extra += (r.flags.num_arrays + r.flags.num_args) * sizeof(Expr*); return extra; } @@ -322,7 +385,7 @@ public fn bool TypeRef.isU32(const TypeRef* r) { return r.isBuiltin() && r.flags.num_ptrs == 0 && r.flags.num_arrays == 0 && r.getBuiltinKind() == BuiltinKind.UInt32; } -fn bool TypeRef.hasPrefix(const TypeRef* r) { +public fn bool TypeRef.hasPrefix(const TypeRef* r) @(unused) { return r.flags.has_prefix; } @@ -330,10 +393,12 @@ public fn bool TypeRef.isIncrArray(const TypeRef* r) { return r.flags.incr_array; } +#if 0 public fn bool TypeRef.isPointerTo(const TypeRef* r, u32 ptr_idx) { if (r.dest != ptr_idx || ptr_idx == 0) return false; return (r.flags.num_ptrs == 1 && r.flags.num_arrays == 0 && r.isUser()); } +#endif public fn BuiltinKind TypeRef.getBuiltinKind(const TypeRef* r) { return (BuiltinKind)r.flags.builtin_kind; @@ -417,7 +482,42 @@ public fn Expr** TypeRef.getArray2(TypeRef* r, u32 idx) { return &arrays[idx]; } -fn void TypeRef.printLiteral(const TypeRef* r, string_buffer.Buf* out, bool print_prefix) { +fn void TypeRef.setArray(TypeRef* r, u32 idx, Expr* d) { + const u32 numrefs = r.isUser() + r.flags.has_prefix; + const u8* ptr = (u8*)r.refs + numrefs * sizeof(Ref); + Expr** arrays = (Expr**)ptr; + arrays[idx] = d; +} + +public fn bool TypeRef.isTemplate(const TypeRef* r) { + return r.flags.num_args != 0; +} + +public fn u32 TypeRef.getNumArgs(const TypeRef* r) { + return r.flags.num_args; +} + +public fn Expr** TypeRef.getArgs(TypeRef* r) { + const u32 numrefs = r.isUser() + r.flags.has_prefix; + const u8* ptr = (u8*)r.refs + numrefs * sizeof(Ref); + return (Expr**)ptr + r.flags.num_arrays; +} + +public fn Expr* TypeRef.getArg(const TypeRef* r, u32 idx) { + const u32 numrefs = r.isUser() + r.flags.has_prefix; + const u8* ptr = (u8*)r.refs + numrefs * sizeof(Ref); + Expr** args = (Expr**)ptr + r.flags.num_arrays; + return args[idx]; +} + +fn void TypeRef.setArg(TypeRef* r, u32 idx, Expr* d) { + const u32 numrefs = r.isUser() + r.flags.has_prefix; + const u8* ptr = (u8*)r.refs + numrefs * sizeof(Ref); + Expr** args = (Expr**)ptr + r.flags.num_arrays; + args[idx] = d; +} + +public fn void TypeRef.printLiteral(const TypeRef* r, string_buffer.Buf* out, bool print_prefix) { if (r.isConst()) out.add("const "); if (r.isVolatile()) out.add("volatile "); @@ -436,6 +536,14 @@ fn void TypeRef.printLiteral(const TypeRef* r, string_buffer.Buf* out, bool prin } else { out.add(d.getName()); } + if (u32 num_args = r.getNumArgs()) { + out.add1('<'); + for (u32 i = 0; i < num_args; i++) { + if (i) out.add(", "); + r.getArg(i).printLiteral(out); + } + out.add1('>'); + } break; case Function: out.add("FN TODO"); @@ -476,6 +584,14 @@ fn void TypeRef.print(const TypeRef* r, string_buffer.Buf* out, bool filled) { out.add1('.'); } out.add(idx2name(r.refs[0].name_idx)); + if (u32 num_args = r.getNumArgs()) { + out.add1('<'); + for (u32 i = 0; i < num_args; i++) { + if (i) out.add(", "); + r.getArg(i).printLiteral(out); + } + out.add1('>'); + } break; case Function: out.add("fn "); @@ -517,6 +633,7 @@ public fn void TypeRef.dump_full(const TypeRef* r) @(unused) { out.indent(1); out.add("flags:"); if (r.flags.is_const) out.add(" const"); + out.print(" args=%d", r.flags.num_args); out.print(" ptrs=%d", r.flags.num_ptrs); out.print(" kind=%s", TypeRefKind.str((TypeRefKind)r.flags.kind)); out.print(" has_prefix=%d", r.flags.has_prefix); diff --git a/ast/utils.c2 b/ast/utils.c2 index 8ef593ef8..909f146e0 100644 --- a/ast/utils.c2 +++ b/ast/utils.c2 @@ -35,7 +35,7 @@ static_assert(32, sizeof(FunctionTypeDecl)); static_assert(32, sizeof(VarDecl)); static_assert(40, sizeof(EnumConstantDecl)); static_assert(40, sizeof(ImportDecl)); -static_assert(48, sizeof(StructTypeDecl)); +static_assert(56, sizeof(StructTypeDecl)); static_assert(80, sizeof(FunctionDecl)); static_assert(8, sizeof(Stmt)); @@ -226,7 +226,7 @@ public fn QualType getBuiltinQT(BuiltinKind kind) { return globals.builtins[kind]; } -fn u32 addAST(AST* ast_) { +fn u16 addAST(AST* ast_) { if (globals.ast_count >= globals.ast_capacity) { if (globals.ast_capacity == 0) globals.ast_capacity = 16; else globals.ast_capacity *= 2; @@ -243,7 +243,7 @@ fn u32 addAST(AST* ast_) { u32 idx = globals.ast_count; globals.ast_list[idx] = ast_; globals.ast_count++; - return idx; + return (u16)idx; } /* diff --git a/ast/value.c2 b/ast/value.c2 index b958b8d50..a9f641c51 100644 --- a/ast/value.c2 +++ b/ast/value.c2 @@ -114,7 +114,7 @@ public fn bool Value.isDecimal(const Value* v) { return v.kind == ValueKind.Integer; } -fn bool Value.isError(const Value* v) { +public fn bool Value.isError(const Value* v) { return v.kind == ValueKind.Error; } @@ -877,7 +877,7 @@ public fn const char* Value.str(const Value* v) { ftoa(out, 64, v.fvalue); break; default: - return "<>"; + return "<>"; } return out; } diff --git a/ast/var_decl.c2 b/ast/var_decl.c2 index 433ae4b9e..5df5be534 100644 --- a/ast/var_decl.c2 +++ b/ast/var_decl.c2 @@ -152,12 +152,10 @@ public fn VarDecl* VarDecl.createStructMember(ast_context.Context* c, fn VarDecl* VarDecl.instantiate(const VarDecl* vd, Instantiator* inst) { - bool matches = vd.typeRef.matchesTemplate(inst.template_name); - u32 extra = matches ? inst.ref.getExtraSize() : vd.typeRef.getExtraSize(); + u32 size = sizeof(VarDecl) + vd.typeRef.instantiateExtraSize(inst); //TODO handle embed - if (vd.base.varDeclBits.has_init) extra += sizeof(VarDeclInit); - if (vd.base.varDeclBits.is_bitfield) extra += sizeof(BitFieldInfo); - u32 size = sizeof(VarDecl) + extra; + if (vd.base.varDeclBits.has_init) size += sizeof(VarDeclInit); + if (vd.base.varDeclBits.is_bitfield) size += sizeof(BitFieldInfo); VarDecl* vd2 = inst.c.alloc(size); vd2.base = vd.base; diff --git a/ast_utils/color.c2 b/ast_utils/color.c2 index 88d6f3aea..9acb8d429 100644 --- a/ast_utils/color.c2 +++ b/ast_utils/color.c2 @@ -22,7 +22,7 @@ public type Color enum u8 { Red, Green, Yellow, - Blue, + DarkBlue, Magenta, Cyan, Grey, @@ -30,7 +30,7 @@ public type Color enum u8 { Bred, Bgreen, Byellow, - Bblue, + Blue, Bmagenta, Bcyan, White, @@ -41,7 +41,7 @@ public const Color Black = Black; public const Color Red = Red; public const Color Green = Green; public const Color Yellow = Yellow; -public const Color Blue = Blue; +public const Color DarkBlue = DarkBlue; public const Color Magenta = Magenta; public const Color Cyan = Cyan; public const Color Grey = Grey; @@ -49,7 +49,7 @@ public const Color Darkgrey = Darkgrey; public const Color Bred = Bred; public const Color Bgreen = Bgreen; public const Color Byellow = Byellow; -public const Color Bblue = Bblue; +public const Color Blue = Blue; public const Color Bmagenta = Bmagenta; public const Color Bcyan = Bcyan; public const Color White = White; @@ -61,7 +61,7 @@ const char*[elemsof(Color)] defaultColors = { [Color.Red] = "\033[0;31m", [Color.Green] = "\033[0;32m", [Color.Yellow] = "\033[0;33m", - [Color.Blue] = "\033[0;34m", + [Color.DarkBlue] = "\033[0;34m", [Color.Magenta] = "\033[0;35m", [Color.Cyan] = "\033[0;36m", [Color.Grey] = "\033[0;37m", @@ -69,7 +69,7 @@ const char*[elemsof(Color)] defaultColors = { [Color.Bred] = "\033[1;31m", [Color.Bgreen] = "\033[1;32m", [Color.Byellow] = "\033[1;33m", - [Color.Bblue] = "\033[1;34m", + [Color.Blue] = "\033[1;34m", [Color.Bmagenta] = "\033[1;35m", [Color.Bcyan] = "\033[1;36m", [Color.White] = "\033[1;37m", diff --git a/ast_utils/string_buffer.c2 b/ast_utils/string_buffer.c2 index 4b193ee7e..0cac65154 100644 --- a/ast_utils/string_buffer.c2 +++ b/ast_utils/string_buffer.c2 @@ -146,6 +146,19 @@ public fn void Buf.add(Buf* buf, const char* text) { buf.add2(text, len); } +// append a possibly qualified name or number as part of a mangled name +public fn void Buf.addMangledName(Buf* buf, const char* text, bool member) { + //buf.print("%d", (u32)strlen(text)); + if (member) buf.add1('$'); + char first = *text; + if (first == '-') text++; + while (char c = *text++) { + if (c == '.') c = '_'; + buf.add1(c); + } + if (first == '-') buf.add1('n'); +} + public fn void Buf.add2(Buf* buf, const char* text, u32 len) { if (buf.size_ + len + 1 > buf.capacity) { // truncate if resize fails. diff --git a/common/ast_builder.c2 b/common/ast_builder.c2 index 2cb163f8c..b9bd17156 100644 --- a/common/ast_builder.c2 +++ b/common/ast_builder.c2 @@ -34,7 +34,7 @@ public type Builder struct @(opaque) { component.Component* comp; // no ownership Module* mod; // no ownership AST* ast; - u32 ast_idx; + u16 ast_idx; u32 c2_name; u32 main_name; bool is_interface; @@ -220,7 +220,8 @@ public fn StructTypeDecl* Builder.actOnStructType(Builder* b, bool is_struct, bool is_global, Decl** members, - u32 num_members) + u32 num_members, + TemplateSpec* template_spec) { is_public |= b.is_interface; StructTypeDecl* d = StructTypeDecl.create(b.context, @@ -231,7 +232,8 @@ public fn StructTypeDecl* Builder.actOnStructType(Builder* b, is_struct, is_global, members, - num_members); + num_members, + template_spec); if (is_global) { b.ast.addTypeDecl(d.asDecl()); b.addSymbol(name, d.asDecl()); @@ -727,7 +729,8 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b, const Ref* prefix, VarDecl** params, u32 num_params, - bool is_variadic) + bool is_variadic, + TemplateSpec* template_spec) { is_public |= b.is_interface; FunctionDecl* f = FunctionDecl.create(b.context, @@ -740,43 +743,14 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b, params, num_params, is_variadic, - DefKind.Global); + template_spec ? DefKind.Template : DefKind.Global, + template_spec); b.ast.addFunc(f); if (!prefix) b.addSymbol(name, f.asDecl()); if (b.is_interface) f.asDecl().setExternal(); return f; } -public fn FunctionDecl* Builder.actOnTemplateFunctionDecl(Builder* b, - u32 name, - SrcLoc loc, - bool is_public, - const TypeRefHolder* rtype, - u32 template_name, - SrcLoc template_loc, - VarDecl** params, - u32 num_params, - bool is_variadic) -{ - if (b.is_interface) b.diags.error(loc, "template functions are not allow in interfaces"); - is_public |= b.is_interface; - FunctionDecl* f = FunctionDecl.createTemplate(b.context, - name, - loc, - is_public, - b.ast_idx, - rtype, - template_name, - template_loc, - params, - num_params, - is_variadic); - b.ast.addFunc(f); - b.addSymbol(name, f.asDecl()); - if (b.is_interface) f.asDecl().setExternal(); - return f; -} - public fn void Builder.actOnFunctionBody(Builder*, FunctionDecl* f, CompoundStmt* body) { f.setBody(body); } @@ -1001,23 +975,10 @@ public fn Expr* Builder.actOnArraySubscriptExpr(Builder* b, SrcLoc loc, u32 src_ return (Expr*)ArraySubscriptExpr.create(b.context, loc, src_len, base, idx); } -public fn Expr* Builder.actOnCallExpr(Builder* b, - SrcLoc loc, SrcLoc endLoc, - Expr* func, - Expr** args, - u32 num_args) { +public fn Expr* Builder.actOnCallExpr(Builder* b, SrcLoc loc, SrcLoc endLoc, Expr* func, Expr** args, u32 num_args) { return (Expr*)CallExpr.create(b.context, loc, endLoc, func, args, num_args); } -public fn Expr* Builder.actOnTemplateCallExpr(Builder* b, - SrcLoc loc, SrcLoc endLoc, - Expr* func, - Expr** args, - u32 num_args, - const TypeRefHolder* ref) { - return (Expr*)CallExpr.createTemplate(b.context, loc, endLoc, func, args, num_args, b.ast_idx, ref); -} - public fn Expr* Builder.actOnExplicitCast(Builder* b, SrcLoc loc, u32 src_len, const TypeRefHolder* ref, @@ -1089,3 +1050,26 @@ public fn Expr* Builder.actOnAlternate(Builder* b, Expr* original, Expr* generat return (Expr*)AlternateExpr.create(b.context, original, generated); } +public fn TemplateSpec* Builder.actOnTemplateSpec(Builder* b, + SrcLoc loc, + bool is_public, + TemplateVar* template_vars, + u32 template_len) +{ + is_public |= b.is_interface; + return TemplateSpec.create(b.context, + loc, + is_public, + b.ast_idx, + template_vars, + template_len); +} + +public fn Expr* Builder.actOnTemplateExpr(Builder* b, + SrcLoc loc, SrcLoc endLoc, + Expr* func, + Expr** args, + u32 num_args) +{ + return (Expr*)TemplateExpr.create(b.context, loc, endLoc, func, b.ast_idx, args, num_args); +} diff --git a/generator/ast_visitor.c2 b/generator/ast_visitor.c2 index 78bbcc886..38e0a802e 100644 --- a/generator/ast_visitor.c2 +++ b/generator/ast_visitor.c2 @@ -53,6 +53,7 @@ public fn void Visitor.handle(Visitor* v, Decl* d) { break; case StructType: StructTypeDecl* s = (StructTypeDecl*)d; + if (s.isTemplate()) break; u32 num_members = s.getNumMembers(); Decl** members = s.getMembers(); for (u32 i=0; i if external, prefix // - struct function a.foo(..) A.foo(..) -> if external, prefix // - via pointer -> (..) -> .. - // - template -> not implemented yet - - assert(!call.isTemplateCall()); // TODO Expr* func = call.getFunc(); assert(func.getKind() == ExprKind.ImplicitCast); @@ -248,12 +248,26 @@ fn void Generator.emitCall(Generator* gen, string_buffer.Buf* out, const CallExp out.lparen(); // arguments - u32 num_args = call.getNumArgs(); - Expr** args = ((CallExpr*)call).getArgs(); - for (u32 i=0; i'); +} diff --git a/generator/ir/ir_generator.c2 b/generator/ir/ir_generator.c2 index ff1f42c08..f61432f5f 100644 --- a/generator/ir/ir_generator.c2 +++ b/generator/ir/ir_generator.c2 @@ -296,6 +296,7 @@ fn void Generator.emitInit(Generator* gen, const Expr* e, u32 size) { gen.ctx.addInitSymbol(sid); break; case Type: + case Template: case Call: e.dump(); assert(0); // should be CTV diff --git a/generator/ir/ir_generator_expr.c2 b/generator/ir/ir_generator_expr.c2 index 7816b2b93..769fc756e 100644 --- a/generator/ir/ir_generator_expr.c2 +++ b/generator/ir/ir_generator_expr.c2 @@ -50,6 +50,7 @@ fn void Generator.emitExpr(Generator* gen, ir.Ref* result, const Expr* e) { gen.emitIdentifier(result, e); break; case Type: + case Template: assert(0); break; case Call: @@ -341,6 +342,7 @@ fn void Generator.emitCond(Generator* gen, const Expr* e, BlockId true_blk, Bloc assert(0); // should have cast? break; case Type: + case Template: assert (0); break; case Call: diff --git a/ir/print.c2 b/ir/print.c2 index 0a5fd6fe7..3f02abf41 100644 --- a/ir/print.c2 +++ b/ir/print.c2 @@ -28,7 +28,7 @@ Color col_PseudoInstr = Bred; Color col_Decl = Magenta; Color col_Name = White; Color col_Block = Cyan; -Color col_Symbol = Bblue; +Color col_Symbol = Blue; Color col_Attr = Bmagenta; Color col_Unknown = Yellow; Color col_Value = Magenta; diff --git a/parser/c2_parser.c2 b/parser/c2_parser.c2 index 29a25ee22..7a89d3a1b 100644 --- a/parser/c2_parser.c2 +++ b/parser/c2_parser.c2 @@ -67,6 +67,9 @@ public type Parser struct @(opaque) { AttrRegistry attr_registry; + bool is_public; + TemplateSpec* template_spec; + stmt_list.List* stmt_lists; u32 stmt_list_count; @@ -273,14 +276,25 @@ fn void Parser.errorAt(Parser* p, SrcLoc loc, const char* format @(printf_format longjmp(&p.jmpbuf, 1); } +fn bool Parser.isLowerName(Parser* p, u32 name_idx) { + if (!name_idx) return false; + const char* name = p.pool.idx2str(name_idx); + return islower(*name); +} + +fn bool Parser.isUpperName(Parser* p, u32 name_idx) { + if (!name_idx) return false; + const char* name = p.pool.idx2str(name_idx); + return isupper(*name); +} + fn void Parser.parseModule(Parser* p, bool is_interface, bool is_generated) { p.expectAndConsume(Kind.KW_module); // accept builtin types as module names if (!p.tok.kind.isBuiltinTypeOrVoid()) p.expectIdentifier(); - const char*modname = p.pool.idx2str(p.tok.name_idx); - if (!ctype.islower(modname[0])) { + if (!p.isLowerName(p.tok.name_idx)) { p.error("a module name must start with a lower case character"); } p.builder.actOnModule(p.tok.name_idx, @@ -331,6 +345,56 @@ fn void Parser.parseImports(Parser* p) { } } +fn void Parser.parseTemplate(Parser* p, bool is_public) { + if (p.template_spec) p.error("nested template specification not allowed"); + + SrcLoc loc = p.tok.loc; + p.consumeToken(); + + bool was_public = p.is_public; + p.is_public = is_public; + + u32 num_vars = 0; + TemplateVar[MaxTemplateArg] vars; + + // TODO: decide on syntax. We currently accept () and <> + Kind sep = p.tok.kind; + if (sep == Less) p.consumeToken(); + else p.expectAndConsume(LParen); + + for (;;) { + if (num_vars >= MaxTemplateArg) { + p.error("too many template arguments"); + } + p.expectIdentifier(); + vars[num_vars] = { .name = p.tok.name_idx, .loc = p.tok.loc } + p.consumeToken(); + if (p.tok.kind == Kind.Equal) { + p.consumeToken(); + vars[num_vars].def = p.parseTypeOrExpr(true); + } + num_vars++; + if (p.tok.kind != Kind.Comma) break; + p.consumeToken(); + } + + if (sep == Less) p.expectAndConsume(Greater); + else p.expectAndConsume(RParen); + + p.template_spec = p.builder.actOnTemplateSpec(p.tok.loc, is_public, vars, num_vars); + if (p.tok.kind == Kind.LBrace) { + p.consumeToken(); + while (!p.tok.done && p.tok.kind != Kind.RBrace) { + p.parseTopLevel(); + } + p.expectAndConsume(Kind.RBrace); + } else { + p.parseTopLevel(); + } + p.is_public = was_public; + p.template_spec = nil; +} + fn void Parser.parseTopLevel(Parser* p) { p.builder.clearAttributes(); @@ -349,6 +413,9 @@ fn void Parser.parseTopLevel(Parser* p) { if (is_public) p.error("static_assert cannot be public"); p.parseStaticAssert(); break; + case KW_template: + p.parseTemplate(is_public); + break; case KW_type: p.parseTypeDecl(is_public); break; @@ -483,36 +550,34 @@ fn void Parser.parseFuncDecl(Parser* p, bool is_public) { bool is_variadic = p.parseFunctionParams(¶ms, is_public, true); - FunctionDecl* f; + TemplateSpec* template_spec = prefix ? nil : p.template_spec; + if (p.tok.kind == Kind.KW_template) { + if (template_spec) { + p.error("redundant template specification"); + } + if (prefix) { + // TODO: support this + p.error("template type functions not supported"); + } + SrcLoc template_loc = p.tok.loc; p.consumeToken(); p.expectIdentifier(); - - u32 template_name = p.tok.name_idx; - SrcLoc template_loc = p.tok.loc; + TemplateVar template_var = { .name = p.tok.name_idx, .loc = p.tok.loc } p.consumeToken(); - // TODO parse multiple template args - - f = p.builder.actOnTemplateFunctionDecl(func_name, - func_loc, - is_public, - &rtype, - template_name, - template_loc, - (VarDecl**)params.getDecls(), - params.size(), - is_variadic); - } else { - f = p.builder.actOnFunctionDecl(func_name, - func_loc, - is_public, - &rtype, - prefix, - (VarDecl**)params.getDecls(), - params.size(), - is_variadic); + template_spec = p.builder.actOnTemplateSpec(template_loc, false, &template_var, 1); } + FunctionDecl* f = p.builder.actOnFunctionDecl(func_name, + func_loc, + is_public, + &rtype, + prefix, + (VarDecl**)params.getDecls(), + params.size(), + is_variadic, + template_spec); + params.free(); u32 num_attr = p.parseOptionalAttributes(); @@ -521,6 +586,9 @@ fn void Parser.parseFuncDecl(Parser* p, bool is_public) { if (p.is_interface) { if (p.tok.kind == Kind.Semicolon) { // function without body (eg. i32 printf(..); ) is allowed + if (template_spec) { + p.diags.error(func_loc, "template functions are not allowed in interfaces"); + } p.consumeToken(); return; } @@ -749,7 +817,7 @@ fn bool Parser.parseOptionalAccessSpecifier(Parser* p) { p.consumeToken(); return true; } - return false; + return p.is_public; } fn u32 Parser.parseOptionalTypeQualifier(Parser* p) { @@ -809,6 +877,18 @@ fn void Parser.parseSingleTypeSpecifier(Parser* p, TypeRefHolder* ref) { p.parseFullTypeIdentifier(ref); if (ref.user.name_idx == p.va_list_idx) p.addImplicitImport(p.varargs_idx, true); + if (p.tok.kind == Less) { + p.consumeToken(); + for (;;) { + Expr* arg = p.parseTypeOrExpr(true); + if (!ref.addArg(arg)) { + p.errorAt(arg.getStartLoc(), "too many template arguments"); + } + if (p.tok.kind != Comma) break; + p.consumeToken(); + } + p.expectAndConsume(Greater); + } } else { p.error("expected type specifier"); } diff --git a/parser/c2_parser_expr.c2 b/parser/c2_parser_expr.c2 index c786d7253..9a9f8d4aa 100644 --- a/parser/c2_parser_expr.c2 +++ b/parser/c2_parser_expr.c2 @@ -23,7 +23,6 @@ import number_radix local; import token local; import src_loc local; -import ctype local; import string; fn Expr* Parser.parseExpr(Parser* p) { @@ -247,7 +246,7 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp Kind savedKind = p.tok.kind; Expr* res = nil; - bool couldBeTemplateCall = false; + bool couldBeTemplateExpr = false; switch (CastExprTokenLookup[savedKind]) { case 0: // other @@ -258,10 +257,12 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp // parse them all in a single MemberExpr if (p.peekToken(1) == Kind.Dot) { res = p.parsePureMemberExpr(); + //couldBeTemplateExpr = !p.isUpperName(*((MemberExpr*)res).getLastMemberNameIdx()); } else { + //couldBeTemplateExpr = !p.isUpperName(p.tok.name_idx); res = p.parseIdentifier().asExpr(); } - couldBeTemplateCall = true; + couldBeTemplateExpr = true; /* // Make sure to pass down the right value for isAddressOfOperand. if (isAddressOfOperand && isPostfixExpressionSuffixStart()) @@ -338,16 +339,15 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp } break; } - return p.parsePostfixExprSuffix(res, couldBeTemplateCall); + return p.parsePostfixExprSuffix(res, couldBeTemplateExpr); } -fn Expr* Parser.parsePostfixExprSuffix(Parser* p, Expr* lhs, bool couldBeTemplateCall) { +fn Expr* Parser.parsePostfixExprSuffix(Parser* p, Expr* lhs, bool couldBeTemplateExpr) { while (1) { switch (p.tok.kind) { case Identifier: return lhs; case LParen: - // C2: can only be call expr? lhs = p.parseCallExpr(lhs); break; case LSquare: @@ -377,23 +377,29 @@ fn Expr* Parser.parsePostfixExprSuffix(Parser* p, Expr* lhs, bool couldBeTemplat p.consumeToken(); break; case Less: - if (couldBeTemplateCall && p.isTemplateFunctionCall()) { + if (couldBeTemplateExpr && p.isTemplateExpr()) { + SrcLoc loc = p.tok.loc; p.consumeToken(); - - TypeRefHolder ref.init(); - p.parseTypeSpecifier(&ref); - - p.expectAndConsume(Kind.Greater); - if (p.tok.kind != Kind.LParen) { - p.error("missing argument list for template function call"); + u32 num_args = 0; + Expr*[MaxTemplateArg] args; + for (;;) { + if (num_args >= MaxTemplateArg) { + p.error("too many template arguments"); + } + args[num_args++] = p.parseTypeOrExpr(true); + if (p.tok.kind != Kind.Comma) break; + p.consumeToken(); } - lhs = p.parseTemplateCallExpr(lhs, &ref); + SrcLoc endLoc = p.tok.loc + 1; + p.expectAndConsume(Kind.Greater); + lhs = p.builder.actOnTemplateExpr(loc, endLoc, lhs, args, num_args); break; } return lhs; default: return lhs; } + couldBeTemplateExpr = false; } } @@ -429,26 +435,6 @@ fn Expr* Parser.parseCallExpr(Parser* p, Expr* func) { return res; } -fn Expr* Parser.parseTemplateCallExpr(Parser* p, Expr* func, const TypeRefHolder* ref) { - SrcLoc loc = p.tok.loc; - p.consumeToken(); // '(' - - ExprList args.init(); - - while (p.tok.kind != Kind.RParen) { - - args.add(p.parseExpr()); - - if (p.tok.kind != Kind.Comma) break; - p.consumeToken(); - } - SrcLoc endLoc = p.tok.loc + 1; - p.expectAndConsume(Kind.RParen); - Expr* res = p.builder.actOnTemplateCallExpr(loc, endLoc, func, args.getExprs(), args.size(), ref); - args.free(); - return res; -} - fn Expr* Parser.parseImpureMemberExpr(Parser* p, Expr* base) { // a[x].b.c, a[x] is base p.consumeToken(); // dot @@ -564,7 +550,7 @@ fn Expr* Parser.parseParenExpr(Parser* p) { return p.builder.actOnParenExpr(loc, src_len, res); } -fn bool Parser.isTemplateFunctionCall(Parser* p) { +fn bool Parser.isTemplateExpr(Parser* p) { assert(p.tok.kind == Kind.Less); // check if tokens after < could be type, followed by >( // longest token line could be mod.type** @@ -600,25 +586,32 @@ fn bool Parser.isTemplateFunctionCall(Parser* p) { return false; } -fn Expr* Parser.parseSizeof(Parser* p) { - SrcLoc loc = p.tok.loc; - p.consumeToken(); - - p.expectAndConsume(Kind.LParen); - - Expr* res = nil; +fn Expr* Parser.parseTypeOrExpr(Parser* p, bool is_template) { if (p.parseAsType()) { // argument is unambiguously a type SrcLoc type_loc = p.tok.loc; TypeRefHolder ref.init(); p.parseTypeSpecifier(&ref); u32 src_len = p.prev_loc - type_loc; - res = p.builder.actOnTypeExpr(type_loc, src_len, &ref); + return p.builder.actOnTypeExpr(type_loc, src_len, &ref); } else { // argument is an expression or an ambiguous name, parse as expression - res = p.parseExpr(); + if (is_template) { + Expr* lhs = p.parseCastExpr(false, false); + // TODO: pass is_template to only disable > and >> + return p.parseRHSOfBinaryExpression(lhs, Prec.Additive); + } + return p.parseExpr(); } +} +fn Expr* Parser.parseSizeof(Parser* p) { + SrcLoc loc = p.tok.loc; + p.consumeToken(); + + p.expectAndConsume(Kind.LParen); + + Expr* res = p.parseTypeOrExpr(false); u32 src_len = p.tok.loc + 1 - loc; p.expectAndConsume(Kind.RParen); @@ -844,13 +837,15 @@ fn bool Parser.parseAsType(Parser* p) { for (;; ahead++) { switch (p.peekToken(ahead)) { case RParen: + case Greater: + case Comma: if (stars) return true; // ambiguous: could be a global constant or a type name return false; // resolve in analyser case LSquare: if (stars) return true; // sizeof(MyType*[...]) // if identifier is not uppercase, argument is not a type - if (!isupper(*p.pool.idx2str(t2.name_idx))) return false; + if (!p.isUpperName(t2.name_idx)) return false; // ambiguous: could be a global constant element or an array type return true; // assume array type case Star: @@ -859,7 +854,7 @@ fn bool Parser.parseAsType(Parser* p) { break; case Less: // if identifier is not uppercase, argument is not a type - if (!isupper(*p.pool.idx2str(t2.name_idx))) return false; + if (!p.isUpperName(t2.name_idx)) return false; // ambiguous: could be a parametric type or a comparison with a global constant return true; // assume parametric type case KW_const: @@ -952,8 +947,7 @@ fn u32 Parser.parseAsCastType(Parser* p, u32 ahead, Kind close_tok) { case Star: // ambiguous: (func)*b // for ambiguous combinations, rely on identifier case // (A)(...) is a cast, (a)(...) is a function call - if (!isupper(p.pool.idx2str(t2.name_idx)[0])) - return 0; + if (!p.isUpperName(t2.name_idx)) return 0; return ahead; default: break; diff --git a/parser/c2_parser_stmt.c2 b/parser/c2_parser_stmt.c2 index 015cb0379..4d39335ea 100644 --- a/parser/c2_parser_stmt.c2 +++ b/parser/c2_parser_stmt.c2 @@ -109,13 +109,20 @@ fn bool Parser.isTypeSpec(Parser* p) { // State: 0 = ID1, 1 = ID2, 2 = pointers, 3 = arrays, 4 = hasname u32 state = 0; u32 ahead = 1; + u32 name_idx = p.tok.name_idx; + // TODO check max lookahead otherwise Tokenizer can assert while (1) { switch (p.peekToken(ahead++)) { case Identifier: if (state == 4) return false; + name_idx = p.tok.name_idx; state = 4; break; + case Less: // check for template instantiation + if (state > 1) return false; + if (!p.isUpperName(name_idx)) return false; + return true; case LSquare: if (state == 4) return false; ahead = p.skipArray(ahead, RSquare); diff --git a/parser/c2_parser_type.c2 b/parser/c2_parser_type.c2 index 9792db01b..574112636 100644 --- a/parser/c2_parser_type.c2 +++ b/parser/c2_parser_type.c2 @@ -94,7 +94,8 @@ fn void Parser.parseStructType(Parser* p, bool is_struct, u32 name, SrcLoc loc, is_struct, true, members.getDecls(), - members.size()); + members.size(), + p.template_spec); members.free(); p.applyAttributes((Decl*)d, num_attr); @@ -145,7 +146,8 @@ fn void Parser.parseStructBlock(Parser* p, DeclList* members, bool is_public) { is_struct, false, sub_members.getDecls(), - sub_members.size()); + sub_members.size(), + nil); sub_members.free(); members.add(member.asDecl()); continue; diff --git a/plugins/unit_test_plugin.c2 b/plugins/unit_test_plugin.c2 index 7d7e98b43..188a10d21 100644 --- a/plugins/unit_test_plugin.c2 +++ b/plugins/unit_test_plugin.c2 @@ -134,9 +134,8 @@ fn void generate_tests(void* arg) { console.debug("unittest: generating tests"); u64 t1 = utils.now(); - Decl** decls = p.decls.getDecls(); for (u32 i=0; i(1, 2); // @error{unknown type 'Foo'} } diff --git a/test/template/samples/max.c2 b/test/template/samples/max.c2 new file mode 100644 index 000000000..7a85e9878 --- /dev/null +++ b/test/template/samples/max.c2 @@ -0,0 +1,36 @@ +// @warnings{no-unused} +module test; + +import stdio local; + +public template { + fn X max(X a, X b) { return (a >= b) ? a : b; } + fn X min(X a, X b) { return (a <= b) ? a : b; } + fn X abs(X a) { return (a < 0) ? -a : a; } +} + +// multiple template arguments not supported yet +public template(X = const char*, Y = usize) { + fn X get(X* a, Y idx) { return a[idx]; } +} + +// template integer arguments not supported yet +public template(X, Min = 0, Max = 1000) { + fn X clamp(X a) { return a < Min ? Min : a > Max ? Max : a; } +} + +public fn i32 main() { + i32 status = 0; + i32 a1 = max(0, 1); + status += (a1 == 1); + u64 a2 = min(0, 1); + status += (a2 == 0); + i32 a3 = max(0, 1); + status += (a3 == 1); + char[] str = "ab"; + const char *a4 = max(str, str + 1); + status += (a4 == str + 1); + //i32 a5 = clamp(100); + //status += (a5 == 100); + return status; +} diff --git a/test/template/samples/vector.c2 b/test/template/samples/vector.c2 new file mode 100644 index 000000000..ca5622a78 --- /dev/null +++ b/test/template/samples/vector.c2 @@ -0,0 +1,112 @@ +// @warnings{no-unused} +module vector; + +import stdio local; +import stdlib local; +import string local; + +//public template(T, N = 4) { +public template(T) { + + // template integer arguments not supported yet + const u32 N = 4; + + type Vector struct { + u32 len; + u32 cap; + T* ptr; + T[N] stash; + } + + fn void Vector.init(Vector* v) { + v.len = 0; + v.len = 0; + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } + + fn void Vector.free(Vector* v) { + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.len = 0; + v.cap = 0; + } + + fn void Vector.add(Vector* v, T e) { + if (v.len >= v.cap) { + if (N > 0 && v.cap == 0) { + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } else { + u32 cap2 = v.cap + v.cap / 2 + 2; + T* vec2 = malloc(cap2 * sizeof(T)); // need const because T is ambiguous + memcpy(vec2, v.ptr, v.len * sizeof(T)); + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.cap = cap2; + v.ptr = vec2; + } + } + v.ptr[v.len] = e; + v.len++; + } + + fn void Vector.clear(Vector* v) { v.len = 0; } + fn u32 Vector.size(const Vector* v) { return v.len; } + + fn T Vector.get(const Vector* v, u32 idx) { + //assert(idx < v.len); + return v.ptr[idx]; + } + + fn T* Vector.getPtr(const Vector* v, u32 idx = 0) { + return &v.ptr[idx]; + } + + fn i32 Vector.printlen(const Vector* v) { + i32 max_len = 1; + for (u32 i = 0; i < v.size(); i++) { + i32 len = snprintf(nil, 0, "%d", v.get(i)); + if (max_len < len) max_len = len; + } + return max_len; + } + + fn void Vector.print(const Vector* v, + const char* prefix = "{", const char* suffix = " }\n", + i32 printlen = 1) + { + fputs(prefix, stdout); + for (u32 i = 0; i < v.size(); i++) { + if (i > 0) putchar(','); + printf(" %*d", printlen, v.get(i)); + } + fputs(suffix, stdout); + } +} + +Vector v0; + +public fn i32 main(i32 argc, char** argv) { + + Vector v1.init(); + Vector v2.init(); + Vector v3.init(); + + if (argc == 1) { + local const char*[] defargs = { "vector", "1", "2", "3", nil } + argc = 4; + argv = (char**)defargs; + } + for (u32 i = 1; i < argc; i++) { + i32 x = atoi(argv[i]); + v1.add(x); + v2.add(x * 10000000000); + v3.add(x * 20000000000); + } + + v0.print("v0 = {", " }\n"); + v1.print("v1 = {", " }\n"); + v2.print("v2 = {", " }\n"); + v3.print("v3 = {", " }\n"); + + return 0; +} diff --git a/test/template/samples/vector2.c2 b/test/template/samples/vector2.c2 new file mode 100644 index 000000000..1f2d1271d --- /dev/null +++ b/test/template/samples/vector2.c2 @@ -0,0 +1,108 @@ +// @warnings{no-unused} +module vector; + +import stdio local; +import stdlib local; +import string local; + +//public template(T, N = 4) { +public template(T) { + + // template integer arguments not supported yet + const u32 N = 4; + + type Vector struct { + u32 len; + u32 cap; + T* ptr; + T[N] stash; + } + + fn void Vector.init(Vector* v) { + v.len = 0; + v.len = 0; + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } + + fn void Vector.free(Vector* v) { + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.len = 0; + v.cap = 0; + } + + fn void Vector.add(Vector* v, T e) { + if (v.len >= v.cap) { + if (N > 0 && v.cap == 0) { + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } else { + u32 cap2 = v.cap + v.cap / 2 + 2; + T* vec2 = malloc(cap2 * sizeof(const T)); // need const because T is ambiguous + memcpy(vec2, v.ptr, v.len * sizeof(const T)); + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.cap = cap2; + v.ptr = vec2; + } + } + v.ptr[v.len] = e; + v.len++; + } + + fn void Vector.clear(Vector* v) { v.len = 0; } + fn u32 Vector.size(const Vector* v) { return v.len; } + + fn T Vector.get(const Vector* v, u32 idx) { + //assert(idx < v.len); + return v.ptr[idx]; + } + + fn T* Vector.getPtr(const Vector* v, u32 idx = 0) { + return &v.ptr[idx]; + } + + fn T Vector.getLast(const Vector* v) { + //assert(idx < v.len); + return v.ptr[v.len - 1]; + } + + fn T* Vector.getLastPtr(const Vector* v) { + return &v.ptr[v.len - 1]; + } +} + +public type V64 Vector; + +fn void print_V64(const V64* v, + const char* prefix = "{", const char* suffix = " }\n", + i32 printlen = 1) +{ + fputs(prefix, stdout); + for (u32 i = 0; i < v.len; i++) { + if (i > 0) putchar(','); + printf(" %*d", printlen, v.get(i)); + } + fputs(suffix, stdout); +} + +fn V64 range_V64(i64 from, i64 to, i64 step = 1) { + V64 v.init(); + for (i64 x = from; x < to; x += step) v.add(x); + return v; +} + +public fn i32 main(i32 argc, char** argv) { + + //Vector > v2d.init(); + Vector v2d.init(); + for (u32 i = 0; i < 10; i++) { + v2d.add(range_V64(i, i + 10)); + } + printf("{\n"); + i32 max_len = snprintf(nil, 0, "%d", v2d.getLastPtr().getLast()); + for (u32 i = 0; i < v2d.size(); i++) { + print_V64(v2d.getPtr(i), " {", " }\n", max_len); + } + printf("}\n"); + return 0; +} diff --git a/test/template/samples/vector3.c2 b/test/template/samples/vector3.c2 new file mode 100644 index 000000000..7a9822f3e --- /dev/null +++ b/test/template/samples/vector3.c2 @@ -0,0 +1,130 @@ +// @warnings{no-unused} +module vector; + +import stdio local; +import stdlib local; +import string local; + +//public template(T, N = 4) { +public template(T) { + + // template integer arguments not supported yet + const u32 N = 4; + + type Vector struct { + u32 len; + u32 cap; + T* ptr; + T[N] stash; + } + + fn void Vector.init(Vector* v) { + v.len = 0; + v.len = 0; + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } + + fn void Vector.free(Vector* v) { + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.len = 0; + v.cap = 0; + } + + fn void Vector.add(Vector* v, T e) { + if (v.len >= v.cap) { + if (N > 0 && v.cap == 0) { + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } else { + u32 cap2 = v.cap + v.cap / 2 + 2; + T* vec2 = malloc(cap2 * sizeof(const T)); // need const because T is ambiguous + memcpy(vec2, v.ptr, v.len * sizeof(const T)); + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.cap = cap2; + v.ptr = vec2; + } + } + v.ptr[v.len] = e; + v.len++; + } + + fn void Vector.clear(Vector* v) { v.len = 0; } + fn u32 Vector.size(const Vector* v) { return v.len; } + + fn T Vector.get(const Vector* v, u32 idx) { + //assert(idx < v.len); + return v.ptr[idx]; + } + + fn const T* Vector.getPtr(const Vector* v, u32 idx = 0) { + return &v.ptr[idx]; + } + + type IntVector struct { Vector base; } + + fn void IntVector.init(IntVector* v) { v.base.init(); } + fn void IntVector.free(IntVector* v) { v.base.free(); } + fn void IntVector.add(IntVector* v, T e) { v.base.add(e); } + fn void IntVector.clear(IntVector* v) { v.base.clear(); } + fn u32 IntVector.size(const IntVector* v) { return v.base.size(); } + fn T IntVector.get(const IntVector* v, u32 idx) { return v.base.get(idx); } + fn const T* IntVector.getPtr(const IntVector* v, u32 idx = 0) { return v.base.getPtr(idx); } + + fn i32 IntVector.printlen(const IntVector* v) { + i32 max_len = 1; + for (u32 i = 0; i < v.size(); i++) { + i32 len = snprintf(nil, 0, "%d", v.get(i)); + if (max_len < len) max_len = len; + } + return max_len; + } + + fn void IntVector.print(const IntVector* v, + const char* prefix = "{", const char* suffix = " }\n", + i32 printlen = 1) + { + fputs(prefix, stdout); + for (u32 i = 0; i < v.size(); i++) { + if (i > 0) putchar(','); + printf(" %*d", printlen, v.get(i)); + } + fputs(suffix, stdout); + } +} + +#if 1 +IntVector v0; + +type V64 IntVector; + +public fn i32 main(i32 argc, char** argv) { + + IntVector v1.init(); + V64 v2.init(); + + for (u32 i = 1; i < argc; i++) { + i32 x = atoi(argv[i]); + v1.add(x); + v2.add(x * 10000000000); + } + + v0.print("v0 = {", " }\n"); + v1.print("v1 = {", " }\n"); + v2.print("v2 = {", " }\n"); + + Vector v2d.init(); + for (u32 i = 0; i < 10; i++) { + V64 v.init(); + for (u32 j = 0; j < 10; j++) v.add(i + j); + v2d.add(v); + } + i32 width = snprintf(nil, 0, "%d", 9 * 9); + printf("{\n"); + for (u32 i = 0; i < v2d.size(); i++) { + v2d.getPtr(i).print(" {", " }\n", width); + } + printf("}\n"); + return 0; +} +#endif diff --git a/test/template/samples/vector4.c2 b/test/template/samples/vector4.c2 new file mode 100644 index 000000000..c7f79bcaf --- /dev/null +++ b/test/template/samples/vector4.c2 @@ -0,0 +1,126 @@ +// @warnings{no-unused} +module vector; + +import stdio local; +import stdlib local; +import string local; + +//public template(T, N = 4) { +public template(T) { + + // template integer arguments not supported yet + const u32 N = 4; + + type Vector struct { + u32 len; + u32 cap; + T* ptr; + T[N] stash; + } + + fn void Vector.init(Vector* v) { + v.len = 0; + v.len = 0; + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } + + fn void Vector.free(Vector* v) { + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.len = 0; + v.cap = 0; + } + + fn void Vector.add(Vector* v, T e) { + if (v.len >= v.cap) { + if (N > 0 && v.cap == 0) { + v.cap = elemsof(v.stash); + v.ptr = v.stash; + } else { + u32 cap2 = v.cap + v.cap / 2 + 2; + T* vec2 = malloc(cap2 * sizeof(const T)); // need const because T is ambiguous + memcpy(vec2, v.ptr, v.len * sizeof(const T)); + if (v.cap > elemsof(v.stash)) free(v.ptr); + v.cap = cap2; + v.ptr = vec2; + } + } + v.ptr[v.len] = e; + v.len++; + } + + fn void Vector.clear(Vector* v) { v.len = 0; } + fn u32 Vector.size(const Vector* v) { return v.len; } + + fn T Vector.get(const Vector* v, u32 idx) { + //assert(idx < v.len); + return v.ptr[idx]; + } + + fn const T* Vector.getPtr(const Vector* v, u32 idx = 0) { + return &v.ptr[idx]; + } + + type IntVector struct { Vector base; } + + fn void IntVector.init(IntVector* v) { v.base.init(); } + fn void IntVector.free(IntVector* v) { v.base.free(); } + fn void IntVector.add(IntVector* v, T e) { v.base.add(e); } + fn void IntVector.clear(IntVector* v) { v.base.clear(); } + fn u32 IntVector.size(const IntVector* v) { return v.base.size(); } + fn T IntVector.get(const IntVector* v, u32 idx) { return v.base.get(idx); } + fn const T* IntVector.getPtr(const IntVector* v, u32 idx = 0) { return v.base.getPtr(idx); } + + fn i32 IntVector.printlen(const IntVector* v) { + i32 max_len = 1; + for (u32 i = 0; i < v.size(); i++) { + i32 len = snprintf(nil, 0, "%d", v.get(i)); + if (max_len < len) max_len = len; + } + return max_len; + } + + fn void IntVector.print(const IntVector* v, + const char* prefix = "{", const char* suffix = " }\n", + i32 printlen = 1) + { + fputs(prefix, stdout); + for (u32 i = 0; i < v.size(); i++) { + if (i > 0) putchar(','); + printf(" %*d", printlen, v.get(i)); + } + fputs(suffix, stdout); + } +} + +IntVector v0; + +public fn i32 main(i32 argc, char** argv) { + + IntVector v1.init(); + IntVector v2.init(); + + for (i32 i = 1; i < argc; i++) { + i32 x = atoi(argv[i]); + v1.add(x); + v2.add(x * 10000000000); + } + + v0.print("v0 = {", " }\n"); + v1.print("v1 = {", " }\n"); + v2.print("v2 = {", " }\n"); + + Vector > v2d.init(); + for (u32 i = 0; i < 10; i++) { + IntVector v.init(); + for (u32 j = 0; j < 10; j++) v.add(i + j); + v2d.add(v); + } + i32 width = snprintf(nil, 0, "%d", 9 * 9); + printf("{\n"); + for (u32 i = 0; i < v2d.size(); i++) { + v2d.getPtr(i).print(" {", " }\n", width); + } + printf("}\n"); + return 0; +}