pkg/compiler: special BASE argument in templates

Signed-off-by: Paul Chaignon <paul.chaignon@orange.com>
diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go
index 24f0a13..7f12d74 100644
--- a/pkg/compiler/check.go
+++ b/pkg/compiler/check.go
@@ -204,7 +204,10 @@
 			} else {
 				// For templates we only do basic checks of arguments.
 				names := make(map[string]bool)
-				for _, arg := range n.Args {
+				for i, arg := range n.Args {
+					if arg.Name == "BASE" && i != len(n.Args)-1 {
+						comp.error(arg.Pos, "type argument BASE must be the last argument")
+					}
 					if names[arg.Name] {
 						comp.error(arg.Pos, "duplicate type argument %v", arg.Name)
 					}
@@ -869,6 +872,17 @@
 		return
 	}
 	typedef := comp.typedefs[typedefName]
+	// Handling optional BASE argument.
+	if len(typedef.Args) > 0 && typedef.Args[len(typedef.Args)-1].Name == "BASE" {
+		if flags&checkIsArg != 0 && len(t.Args) == len(typedef.Args)-1 {
+			t.Args = append(t.Args, &ast.Type{
+				Pos:   t.Pos,
+				Ident: "intptr",
+			})
+		} else if len(t.Args) == len(typedef.Args) {
+			comp.checkTypeArg(t, t.Args[len(t.Args)-1], typeArgBase)
+		}
+	}
 	fullTypeName := ast.SerializeNode(t)
 	for i, prev := range ctx.instantiationStack {
 		if prev == fullTypeName {
@@ -891,6 +905,9 @@
 		if nargs == 0 {
 			comp.error(t.Pos, "type %v is not a template", typedefName)
 		} else {
+			if flags&checkIsArg != 0 && typedef.Args[len(typedef.Args)-1].Name == "BASE" {
+				nargs--
+			}
 			comp.error(t.Pos, "template %v needs %v arguments instead of %v",
 				typedefName, nargs, len(t.Args))
 		}
diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt
index 90d4089..7df52dd 100644
--- a/pkg/compiler/testdata/all.txt
+++ b/pkg/compiler/testdata/all.txt
@@ -213,9 +213,9 @@
 	f1	TYPE
 }
 
-type templ_templ[BASE, CONST] {
-	f1	BASE[int8, CONST]
-	f2	BASE[int32, 0]
+type templ_templ[TMPL, CONST] {
+	f1	TMPL[int8, CONST]
+	f2	TMPL[int32, 0]
 }
 
 templ_templ_use {
@@ -230,6 +230,8 @@
 type templ_struct6 templ_struct0[C1, templ_struct2[C2]]
 type templ_union union_with_templ_struct
 type templ_base3[BASE] BASE
+type templ_base4[BASE] const[0, BASE]
+type templ_base5[VAL, BASE] const[VAL, BASE]
 
 foo$templ0(a templ0[42, int8])
 foo$templ1(a ptr[in, templ_struct0[C2, int8]])
@@ -241,6 +243,8 @@
 foo$templ7(a ptr[in, templ_struct5], b ptr[in, templ_struct6], c ptr[in, templ_union], d ptr[in, type3])
 foo$templ8(a ptr[in, templ_templ_use])
 foo$templ9(a ptr[in, templ_base3[int64]])
+foo$templ10(a ptr[in, templ_base4[int8]])
+foo$templ11(a ptr[in, templ_base5[42, int8]])
 
 # Structs.
 
diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt
index d7d8015..961de44 100644
--- a/pkg/compiler/testdata/errors.txt
+++ b/pkg/compiler/testdata/errors.txt
@@ -149,6 +149,7 @@
 	f6	int32:32
 	f7	int32:33	### bitfield of size 33 is too large for base type of size 32
 	f8	const[0, int32:C1]	### literal const bitfield sizes are not supported
+	f9	const[0]	### wrong number of arguments for type const, expect value, base type
 } [packed, align_4]
 
 s4 {
@@ -282,6 +283,10 @@
 type templ5[abc] ptr[in, abc]	### type argument abc must be ALL_CAPS
 type templ6[T] ptr[in, T]
 type templ7 templ0[templ6, int8]
+type templ8[BASE, A] BASE[A]	### type argument BASE must be the last argument
+type templ9[BASE] BASE
+type templ10[A] templ9[A]
+type templ11[VAL, BASE] const[VAL, BASE]
 
 # Note: here 42 is stripped as base type, so const ends up without arguments.
 foo$201(a templ1[42])		### wrong number of arguments for type const, expect value
@@ -306,12 +311,12 @@
 	f1	TYPE
 }
 
-type templ_templ0[BASE] {
-	f1	BASE
+type templ_templ0[B] {
+	f1	B
 }
 
-type templ_templ1[BASE] {
-	f1	BASE[int16]			### both template parameter BASE and its usage have sub-arguments
+type templ_templ1[B] {
+	f1	B[int16]			### both template parameter B and its usage have sub-arguments
 }
 
 templ_templ_use0 {
@@ -325,6 +330,11 @@
 foo$204(a ptr[in, templ_struct0[42, int8]])
 foo$205(a ptr[in, templ_struct0[int8, int8]])
 foo$207(a ptr[in, templ_struct2[1]])		### template argument A is not used
+foo$208(a ptr[in, templ9[string["foo"]]])	### unexpected value string for base type argument of templ9 type, expect [int8 int16 int32 int64 int16be int32be int64be intptr]
+foo$209(a ptr[in, templ10[templ0[42, int8]]])	### unexpected value templ0 for base type argument of templ9 type, expect [int8 int16 int32 int64 int16be int32be int64be intptr]
+foo$210(a ptr[in, templ11[0, 1, int8]])		### template templ11 needs 2 arguments instead of 3
+foo$211(a ptr[in, templ9])			### template templ9 needs 1 arguments instead of 0
+foo$212(a ptr[in, templ11[1]])			### template templ11 needs 2 arguments instead of 1
 
 # fmt