diff options
-rw-r--r-- | CHANGES.current | 13 | ||||
-rw-r--r-- | Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i | 9 | ||||
-rw-r--r-- | Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr | 2 | ||||
-rw-r--r-- | Examples/test-suite/java/template_partial_specialization_more_runme.java | 49 | ||||
-rw-r--r-- | Examples/test-suite/template_partial_specialization_more.i | 68 | ||||
-rw-r--r-- | Source/CParse/cparse.h | 1 | ||||
-rw-r--r-- | Source/CParse/parser.y | 37 | ||||
-rw-r--r-- | Source/CParse/templ.c | 71 | ||||
-rw-r--r-- | Source/Swig/parms.c | 3 |
9 files changed, 220 insertions, 33 deletions
diff --git a/CHANGES.current b/CHANGES.current index 2c9c85330..f0b916be0 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,19 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.2.0 (in progress) =========================== +2023-03-01: wsfulton + Partial template specialization fixes to support default arguments from the primary + template's parameter list. + + template<class Y, class T=int> struct X { void primary() {} }; + // Previously the specialization below resulted in: + // Error: Inconsistent argument count in template partial specialization. 1 2 + template<class YY> struct X<YY*> { void special(YY*) {} }; + + // Both of these correctly wrap the partially specialized template + %template(StringPtr) X<const char *>; + %template(ShortPtr) X<short *, int>; + 2023-02-15: wsfulton #1300 Further partial template specialization fixes. Fixes when templates are used as a template parameter in a partially specialized diff --git a/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i new file mode 100644 index 000000000..25b2c5c36 --- /dev/null +++ b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.i @@ -0,0 +1,9 @@ +%module xxx + +template<class Y, class T=int> struct X { void primary() {} }; +template<class YY> struct X<YY*> { void special(YY*) {} }; + +%template(Xbad1) X<>; +%template(Xokay1) X<const char *>; +%template(Xokay2) X<const short *, int>; +%template(Xbad2) X<const char *, int, double>; diff --git a/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr new file mode 100644 index 000000000..196862431 --- /dev/null +++ b/Examples/test-suite/errors/cpp_template_partial_specialization_defaults.stderr @@ -0,0 +1,2 @@ +cpp_template_partial_specialization_defaults.i:6: Error: Not enough template parameters specified. Minimum of 1 required. +cpp_template_partial_specialization_defaults.i:9: Error: Too many template parameters. Maximum of 2. diff --git a/Examples/test-suite/java/template_partial_specialization_more_runme.java b/Examples/test-suite/java/template_partial_specialization_more_runme.java new file mode 100644 index 000000000..c990a1bc0 --- /dev/null +++ b/Examples/test-suite/java/template_partial_specialization_more_runme.java @@ -0,0 +1,49 @@ +import template_partial_specialization_more.*; + +public class template_partial_specialization_more_runme { + + static { + try { + System.loadLibrary("template_partial_specialization_more"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void main(String argv[]) { + // (1) + VectInt vi = new VectInt(); + int num = new FooVectIntDouble().partially_specialized(222); + new FooShortPtrDouble().pointer_specialize((short)0); + vi = new FooVectVectInt().partially_specialized(vi); + + // (2) + new HeyInts().special_hey(); + + // (3) + new XX1().special1(); + new XX2().special2(); + new XX3().special3(); + + // (4) + new PartiallerPrimary().primary((short)0, (short)0); + new PartiallerSpecial().special(new PlainStruct(), 999, true); + + // (5) + new LystDouble().primary(11.1, new AllocatorDouble()); + new LystShort().primary((short)0, new AllocatorShort()); + new LystPlainStructPtr().specialized1(new PlainStruct(), new AllocatorPlainStructPtr()); + new LystDoublePtrPtr().specialized2(22.2, (SWIGTYPE_p_p_double)null); + new LystConstIntRef().specialized3(100); + new LystConstStringRef().specialized3("hello"); + + // (6) + SpecDoubleInt d = new SpecDoubleInt(); + SpecStringInt i = new SpecStringInt(); + d.spec_specialized(12.3); + i.spec_specialized("hi"); + template_partial_specialization_more.UseSpec1(d, d); + template_partial_specialization_more.UseSpec2(i, i); + } +} diff --git a/Examples/test-suite/template_partial_specialization_more.i b/Examples/test-suite/template_partial_specialization_more.i index a9e0a591b..e63ac158b 100644 --- a/Examples/test-suite/template_partial_specialization_more.i +++ b/Examples/test-suite/template_partial_specialization_more.i @@ -72,32 +72,70 @@ template<typename S1, typename S2> struct Partialler<S2, S1*> { void special(S1* // (5) Default args used in specialization, like std::list +%include <std_string.i> %inline %{ template <typename A> struct Allocator {}; template <typename T, class Alloc = Allocator<T> > struct Lyst { void primary(T, Allocator<T>) {} }; template <typename TT, class XXAlloc> struct Lyst<TT*, XXAlloc> { void specialized1(TT, XXAlloc) {} }; template <typename TTT, class YY> struct Lyst<TTT**, Allocator<YY> > { void specialized2(TTT, YY) {} }; -// TODO Error: Inconsistent argument count in template partial specialization. 1 2 -//template <typename TTTT> struct Lyst<const TTTT&> { void specialized3(TTTT) {} }; -void test_list() { - int myint = 0; - Lyst<int> lis; - lis.primary(myint, Allocator<int>()); - - PlainStruct ps; - Lyst<PlainStruct *> liss; - liss.specialized1(ps, Allocator<PlainStruct *>()); +template <typename TTTT> struct Lyst<const TTTT&> { void specialized3(TTTT) {} }; +void test_list() { double mydouble = 0; - Lyst<double **> lissd; - lissd.specialized2(mydouble, (double **)0); + Lyst<double>().primary(mydouble, Allocator<double>()); + Lyst<short, Allocator<short> >().primary(mydouble, Allocator<short>()); -// Lyst<const int&> lissconstint; -// lissconstint.specialized3(myint); + PlainStruct ps; + int myint = 0; + std::string mystring = 0; + Lyst<PlainStruct *>().specialized1(ps, Allocator<PlainStruct *>()); + Lyst<double **>().specialized2(mydouble, (double **)0); + Lyst<const int&>().specialized3(myint); + // Specifying the default still calls the partially specialized template + Lyst<std::string const &, Allocator<std::string const &> >().specialized3(mystring); } %} +%template(AllocatorDouble) Allocator<double>; +%template(AllocatorShort) Allocator<short>; +%template(AllocatorPlainStructPtr) Allocator<PlainStruct *>; + %template(LystDouble) Lyst<double>; -//%template(LystDouble) Lyst<short, Allocator<short> >; +%template(LystShort) Lyst<short, Allocator<short> >; %template(LystPlainStructPtr) Lyst<PlainStruct *>; %template(LystDoublePtrPtr) Lyst<double **>; // called specialized1 instead of specialized2 +%template(LystConstIntRef) Lyst<const int&>; +%template(LystConstStringRef) Lyst<const std::string&, Allocator<const std::string&> >; + +%inline %{ +// Both parameters in each of the functions below are the same type +void UseLystDouble(Lyst<double> a, Lyst<double, Allocator<double> > b) {} +void UseLystShort(Lyst<short> a, Lyst<short, Allocator<short> > b) {} +void UseLystPlainStructPtr(Lyst<PlainStruct *> a, Lyst<PlainStruct *, Allocator<PlainStruct *> > b) {} +void UseLystDoublePtrPtr(Lyst<double **> a, Lyst<double **, Allocator<double **> > b) {} +void UseLystConstIntRef(Lyst<const int&> a, Lyst<const int&, Allocator<const int&> > b) {} +void UseLystConstStringRef(Lyst<const std::string&> a, Lyst<const std::string&, Allocator<const std::string&> > b) {} +%} + +// (6) Default args used in specialization, more variations specifying / not specifying default +%inline %{ +template<typename P, typename Q = int> struct Spec { void spec_primary(P p, Q q) {} }; +template<typename PP> struct Spec<const PP&, int> { void spec_specialized(PP pp) {} }; +%} + +%template(SpecDoubleInt) Spec<const double&, int>; +%template(SpecStringInt) Spec<const std::string&>; + +%inline %{ +void UseSpec1(Spec<const double&, int> x, Spec<const double&, int> y) {} +void UseSpec2(Spec<const std::string&, int> x, Spec<const std::string&, int> y) {} +void test_spec() { + double mydouble = 0.0; + Spec<const double&, int>().spec_specialized(mydouble); + Spec<const double&>().spec_specialized(mydouble); + + std::string mystring; + Spec<const std::string&, int>().spec_specialized(mystring); + Spec<const std::string&>().spec_specialized(mystring); +} +%} diff --git a/Source/CParse/cparse.h b/Source/CParse/cparse.h index db050eb91..a7a40c74d 100644 --- a/Source/CParse/cparse.h +++ b/Source/CParse/cparse.h @@ -69,6 +69,7 @@ extern "C" { extern Node *Swig_cparse_template_locate(String *name, ParmList *tparms, String *symname, Symtab *tscope); extern void Swig_cparse_debug_templates(int); extern ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parameters, Node *primary, Node *templ); + extern ParmList *Swig_cparse_template_partialargs_expand(ParmList *partially_specialized_parms, Node *primary, ParmList *templateparms); #ifdef __cplusplus } diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index df2a87a91..10c3e8207 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -4121,10 +4121,10 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { set_nodeType($$,"template"); /* Template partial specialization */ if (tempn && ($3) && ($6)) { - List *tlist; - String *targs = SwigType_templateargs(tname); - tlist = SwigType_parmlist(targs); - /* Printf(stdout,"targs = '%s' %s\n", targs, tlist); */ + ParmList *primary_templateparms = Getattr(tempn, "templateparms"); + String *targs = SwigType_templateargs(tname); /* tname contains name and specialized template parameters, for example: X<(p.T,TT)> */ + List *tlist = SwigType_parmlist(targs); + int specialization_parms_len = Len(tlist); if (!Getattr($$,"sym:weak")) { Setattr($$,"sym:typename","1"); } @@ -4133,13 +4133,15 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { Delattr($$, "specialization"); Setattr($$, "partialspecialization", "1"); - if (Len(tlist) != ParmList_len(Getattr(tempn,"templateparms"))) { - Swig_error(Getfile($$),Getline($$),"Inconsistent argument count in template partial specialization. %d %d\n", Len(tlist), ParmList_len(Getattr(tempn,"templateparms"))); + if (specialization_parms_len > ParmList_len(primary_templateparms)) { + Swig_error(Getfile($$), Getline($$), "Template partial specialization has more arguments than primary template %d %d.\n", specialization_parms_len, ParmList_len(primary_templateparms)); + } else if (specialization_parms_len < ParmList_numrequired(primary_templateparms)) { + Swig_error(Getfile($$), Getline($$), "Template partial specialization has fewer arguments than primary template %d %d.\n", specialization_parms_len, ParmList_len(primary_templateparms)); } else { /* Create a specialized name with template parameters replaced with $ variables, such as, X<(T1,p.T2) => X<($1,p.$2)> */ Parm *p = $3; - String *fname = NewString(Getattr($$,"name")); + String *fname = NewString(tname); String *ffname = 0; ParmList *partialparms = 0; @@ -4184,6 +4186,27 @@ cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN { Append(ffname,")>"); } { + /* Replace each primary template parameter's name and value with $ variables, such as, class Y,class T=Y => class $1,class $2=$1 */ + ParmList *primary_templateparms_copy = CopyParmList(primary_templateparms); + p = primary_templateparms_copy; + i = 0; + while (p) { + String *name = Getattr(p, "name"); + Parm *pp = nextSibling(p); + ++i; + sprintf(tmp, "$%d", i); + while (pp) { + Replaceid(Getattr(pp, "value"), name, tmp); + pp = nextSibling(pp); + } + Setattr(p, "name", NewString(tmp)); + p = nextSibling(p); + } + /* Modify partialparms by adding in missing default values ($ variables) from primary template parameters */ + partialparms = Swig_cparse_template_partialargs_expand(partialparms, tempn, primary_templateparms_copy); + Delete(primary_templateparms_copy); + } + { Node *new_partial = NewHash(); String *partials = Getattr(tempn,"partials"); if (!partials) { diff --git a/Source/CParse/templ.c b/Source/CParse/templ.c index 8c00570be..e9f00c429 100644 --- a/Source/CParse/templ.c +++ b/Source/CParse/templ.c @@ -498,7 +498,6 @@ int Swig_cparse_template_expand(Node *n, String *rname, ParmList *tparms, Symtab p = nextSibling(p); tp = nextSibling(tp); } - assert(ParmList_len(ptargs) == ParmList_len(tparms)); Delete(ptargs); } else { Setattr(n, "templateparmsraw", Getattr(n, "templateparms")); @@ -835,7 +834,7 @@ static Node *template_locate(String *name, Parm *instantiated_parms, String *sym targs = Getattr(templ, "templateparms"); expandedparms = Swig_symbol_template_defargs(parms, targs, tscope, primary_scope); - /* reduce the typedef */ + /* Qualify template parameters */ p = expandedparms; while (p) { SwigType *ty = Getattr(p, "type"); @@ -934,14 +933,13 @@ static Node *template_locate(String *name, Parm *instantiated_parms, String *sym partials = Getattr(templ, "partials"); /* note that these partial specializations do not include explicit specializations */ if (partials) { Iterator pi; - int parms_len = ParmList_len(parms); + int parms_len = ParmList_len(parms); /* max parameters including defaulted parameters from primary template (ie max parameters) */ int *priorities_row; max_possible_partials = Len(partials); priorities_matrix = (int *)Malloc(sizeof(int) * max_possible_partials * parms_len); /* slightly wasteful allocation for max possible matches */ priorities_row = priorities_matrix; for (pi = First(partials); pi.item; pi = Next(pi)) { Parm *p = parms; - int all_parameters_match = 1; int i = 1; Parm *partialparms = Getattr(pi.item, "partialparms"); Parm *pp = partialparms; @@ -950,6 +948,7 @@ static Node *template_locate(String *name, Parm *instantiated_parms, String *sym Printf(stdout, " checking match: '%s' (partial specialization)\n", templcsymname); } if (ParmList_len(partialparms) == parms_len) { + int all_parameters_match = 1; while (p && pp) { SwigType *t; t = Getattr(p, "type"); @@ -1165,15 +1164,18 @@ Node *Swig_cparse_template_locate(String *name, Parm *instantiated_parms, String Parm *tparmsfound = Getattr(primary ? primary : n, "templateparms"); int specialized = !tparmsfound; /* fully specialized (an explicit specialization) */ int variadic = ParmList_variadic_parm(tparmsfound) != 0; + match = n; if (!specialized) { if (!variadic && (ParmList_len(instantiated_parms) > ParmList_len(tparmsfound))) { Swig_error(cparse_file, cparse_line, "Too many template parameters. Maximum of %d.\n", ParmList_len(tparmsfound)); + match = 0; } else if (ParmList_len(instantiated_parms) < ParmList_numrequired(tparmsfound) - (variadic ? 1 : 0)) { /* Variadic parameter is optional */ - Swig_error(cparse_file, cparse_line, "Not enough template parameters specified. %d required.\n", (ParmList_numrequired(tparmsfound) - (variadic ? 1 : 0)) ); + Swig_error(cparse_file, cparse_line, "Not enough template parameters specified. Minimum of %d required.\n", (ParmList_numrequired(tparmsfound) - (variadic ? 1 : 0)) ); + match = 0; } } - SetFlag(n, "instantiate"); - match = n; + if (match) + SetFlag(n, "instantiate"); } else { Node *firstn = 0; /* If not a templated class we must have a templated function. @@ -1286,6 +1288,22 @@ static void use_mark_defaults(ParmList *defaults) { } /* ----------------------------------------------------------------------------- + * use_mark_specialized_defaults() + * + * Modify extra defaulted parameters ready for adding to specialized template parameters list + * ----------------------------------------------------------------------------- */ + +static void use_mark_specialized_defaults(ParmList *defaults) { + Parm *tp = defaults; + while (tp) { + Setattr(tp, "default", "1"); + Setattr(tp, "type", Getattr(tp, "value")); + Delattr(tp, "name"); + tp = nextSibling(tp); + } +} + +/* ----------------------------------------------------------------------------- * expand_defaults() * * Replace parameter types in default argument values, example: @@ -1324,12 +1342,11 @@ static void expand_defaults(ParmList *expanded_templateparms) { * ----------------------------------------------------------------------------- */ ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parms, Node *primary, Node *templ) { - ParmList *expanded_templateparms = 0; + ParmList *expanded_templateparms = CopyParmList(instantiated_parms); if (Equal(Getattr(primary, "templatetype"), "class")) { /* Templated class */ ParmList *templateparms = Getattr(primary, "templateparms"); - expanded_templateparms = CopyParmList(instantiated_parms); int variadic = merge_parameters(expanded_templateparms, templateparms); /* Add default arguments from primary template */ if (!variadic) { @@ -1345,9 +1362,43 @@ ParmList *Swig_cparse_template_parms_expand(ParmList *instantiated_parms, Node * /* Templated function */ /* TODO: Default template parameters support was only added in C++11 */ ParmList *templateparms = Getattr(templ, "templateparms"); - expanded_templateparms = CopyParmList(instantiated_parms); merge_parameters(expanded_templateparms, templateparms); } return expanded_templateparms; } + +/* ----------------------------------------------------------------------------- + * Swig_cparse_template_partialargs_expand() + * + * partially_specialized_parms: partially specialized template parameters + * primary: primary template node + * templateparms: primary template parameters (providing the defaults) + * + * Expand the partially_specialized_parms and return a parameter list with default + * arguments filled in where necessary. + * ----------------------------------------------------------------------------- */ + +ParmList *Swig_cparse_template_partialargs_expand(ParmList *partially_specialized_parms, Node *primary, ParmList *templateparms) { + ParmList *expanded_templateparms = CopyParmList(partially_specialized_parms); + + if (Equal(Getattr(primary, "templatetype"), "class")) { + /* Templated class */ + int variadic = ParmList_variadic_parm(templateparms) ? 1 : 0; + /* Add default arguments from primary template */ + if (!variadic) { + ParmList *defaults_start = ParmList_nth_parm(templateparms, ParmList_len(partially_specialized_parms)); + if (defaults_start) { + ParmList *defaults = CopyParmList(defaults_start); + use_mark_specialized_defaults(defaults); + expanded_templateparms = ParmList_join(expanded_templateparms, defaults); + expand_defaults(expanded_templateparms); + } + } + } else { + /* Templated function */ + /* TODO: Default template parameters support was only added in C++11 */ + } + + return expanded_templateparms; +} diff --git a/Source/Swig/parms.c b/Source/Swig/parms.c index 3e052ca0c..c1dffb572 100644 --- a/Source/Swig/parms.c +++ b/Source/Swig/parms.c @@ -190,7 +190,8 @@ Parm *ParmList_variadic_parm(ParmList *p) { /* ----------------------------------------------------------------------------- * ParmList_numrequired() * - * Return number of required arguments + * Return number of required arguments - the number of arguments excluding + * default arguments * ----------------------------------------------------------------------------- */ int ParmList_numrequired(ParmList *p) { |