Bug 4173 – Regression(2.037) Explicitly instantiated templates still try to do IFTI in some cases

Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
Other
OS
Linux
Creation time
2010-05-10T14:17:00Z
Last change time
2015-06-09T01:28:19Z
Keywords
patch, rejects-valid
Assigned to
nobody
Creator
schveiguy

Attachments

IDFilenameSummaryContent-TypeSize
745templiface.dTemplate Interface tests for test suitetext/plain2801

Comments

Comment #0 by schveiguy — 2010-05-10T14:17:02Z
I'm unsure if this is a minimal case: interface I(T) { T foo(); } interface I2(T, U) { T foo(); U bar(); } void fn(T)(I!T t1) { } void fn(T, U)(I2!(T, U) t1) { } class C : I!int { int foo() { return 5;} } void main() { auto c = new C; I!int i = c; fn!(int)(i); // compiles fn!(int)(c); // fails } output: testtemplate.d(26): Error: template testtemplate.fn(T) does not match any function template declaration testtemplate.d(26): Error: template testtemplate.fn(T) cannot deduce template function from argument types !(int)(C) testtemplate.d(26): Error: template instance errors instantiating template This compiled with 2.036.
Comment #1 by clugdbug — 2010-07-26T11:33:55Z
Reduced test case: class I(X) { } // also for interface void fn(T)(I!T t1) { } void fn(A, B)(int t1) { } class C : I!int { } void bug4173() { C c; fn!(int)(c); }
Comment #2 by clugdbug — 2010-08-30T13:52:14Z
This bug was triggered by svn commit 273, which was related to opDispatch. The immediate change was in CallExp::semantic(). Previously, it attempted full instantiation, and if that failed, it tried partial explicit instantiation. After this change, it calls needsTypeInference(). It does partial instantiation if true, otherwise does full instantiation. In this test case, there is one template which requires partial explicit instantantion, and one which does not. So, in 2.036 and earlier, the full instantiation succeeded, before it even considered the two-argument template. Seems as though the code for explicit instantiation is slightly more capable at the moment, than the partial instantiation code. Bug 4430 is probably another instance of the same bug.
Comment #3 by clugdbug — 2010-09-03T01:45:03Z
It seems to be just luck that this ever worked at all. Template parameter deduction has never worked for base classes or interfaces of a class. So although this particular case used to work, it was very fragile -- very closely related cases would fail. I've only been able to fix this by implementing template parameter deduction for base classes. It allows them as a "match with implicit conversion". Thus, it also fixes bug 1715 "Template specialization checks for equality rather than convertibility" and bug 1970 "Templated interfaces not matched". But I've found a very annoying case: interface I(X) {} class C: I!(char), I!(double) {} void foo(T)(I!(T) x) {} void main() { C c = new C; foo(c); // could match with I!char or I!double } This should be treated as a match-with-implicit-conversion for c, but the type T has not been determined, so template instantiation should fail. If the signature is instead: foo(T)(I!(T) x, T y){} and called with foo(c, 1.2), then instantiation should succeed.
Comment #4 by clugdbug — 2010-09-03T11:19:35Z
This patch is a drop-in replacement for TypeClass::deduceType() in template.c, line 2180 in D1, 2480 in D2. The patch is unchanged between D1 & D2. It fixes these bugs: bug 1715 Template specialization checks for equality rather than convertibility bug 1970 Templated interfaces not matched bug 4173 Regression(2.037) Explicitly instantiated templates still try to do IFTI in some cases It is far more general than any of those bugs. It checks all base interfaces, and detects ambiguities. It copes with some pretty difficult cases: interface TwoWay(A,B) {} class C1: TwoWay!(char, float), TwoWay!(int, float) {} class C2: TwoWay!(int, char), TwoWay!(float, char) {} C1 twoway; C2 twoway2; B foo(A, B)(TwoWay!(A, B) x, TwoWay!(B, A) y) { return B.init;} static assert(is(typeof(foo(twoway, twoway2)) == float)); static assert(is(typeof(foo(twoway2, twoway)) == char)); though it can only do this if it can determine at least one template parameter from the first function parameter. PATCH: Template.c line 2480, replacing TypeClass::deduceType() ---------------------- /* Helper for TypeClass::deduceType(). * Classes can match with implicit conversion to a base class or interface. * This is complicated, because there may be more than one base class which * matches. In such cases, one or more parameters remain ambiguous. * For example, * * interface I(X, Y) {} * class C : I(uint, double), I(char, double) {} * C x; * foo(T, U)( I!(T, U) x) * * deduces that U is double, but T remains ambiguous (could be char or uint). * * Given a baseclass b, and initial deduced types 'dedtypes', this function * tries to match tparam with b, and also tries all base interfaces of b. * If a match occurs, numBaseClassMatches is incremented, and the new deduced * types are ANDed with the current 'best' estimate for dedtypes. */ void deduceBaseClassParameters(BaseClass *b, Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, Objects *best, int &numBaseClassMatches) { TemplateInstance *parti = b->base->parent->isTemplateInstance(); if (parti) { // Make a temporary copy of dedtypes so we don't destroy it Objects *tmpdedtypes = new Objects(); tmpdedtypes->setDim(dedtypes->dim); memcpy(tmpdedtypes->data, dedtypes->data, dedtypes->dim * sizeof(void *)); TypeInstance *t = new TypeInstance(0, parti); MATCH m = t->deduceType(sc, tparam, parameters, tmpdedtypes); if (m != MATCHnomatch) { // If this is the first ever match, it becomes our best estimate if (numBaseClassMatches==0) memcpy(best->data, tmpdedtypes->data, tmpdedtypes->dim * sizeof(void *)); else for (size_t k = 0; k < tmpdedtypes->dim; ++k) { // If we've found more than one possible type for a parameter, // mark it as unknown. if (tmpdedtypes->data[k] != best->data[k]) best->data[k] = dedtypes->data[k]; } ++numBaseClassMatches; } } // Now recursively test the inherited interfaces for (size_t j = 0; j < b->baseInterfaces_dim; ++j) { deduceBaseClassParameters( &(b->baseInterfaces)[j], sc, tparam, parameters, dedtypes, best, numBaseClassMatches); } } MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { //printf("TypeClass::deduceType(this = %s)\n", toChars()); /* If this class is a template class, and we're matching * it against a template instance, convert the class type * to a template instance, too, and try again. */ TemplateInstance *ti = sym->parent->isTemplateInstance(); if (tparam && tparam->ty == Tinstance) { if (ti && ti->toAlias() == sym) { TypeInstance *t = new TypeInstance(0, ti); MATCH m = t->deduceType(sc, tparam, parameters, dedtypes); // Even if the match fails, there is still a chance it could match // a base class. if (m != MATCHnomatch) return m; } /* Match things like: * S!(T).foo */ TypeInstance *tpi = (TypeInstance *)tparam; if (tpi->idents.dim) { Identifier *id = (Identifier *)tpi->idents.data[tpi->idents.dim - 1]; if (id->dyncast() == DYNCAST_IDENTIFIER && sym->ident->equals(id)) { Type *tparent = sym->parent->getType(); if (tparent) { /* Slice off the .foo in S!(T).foo */ tpi->idents.dim--; MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes); tpi->idents.dim++; return m; } } } // If it matches exactly or via implicit conversion, we're done MATCH m = Type::deduceType(sc, tparam, parameters, dedtypes); if (m != MATCHnomatch) return m; /* There is still a chance to match via implicit conversion to * a base class or interface. Because there could be more than one such * match, we need to check them all. */ int numBaseClassMatches = 0; // Have we found an interface match? // Our best guess at dedtypes Objects *best = new Objects(); best->setDim(dedtypes->dim); ClassDeclaration *s = sym; while(s && s->baseclasses->dim > 0) { // Test the base class deduceBaseClassParameters((BaseClass *)(s->baseclasses->data[0]), sc, tparam, parameters, dedtypes, best, numBaseClassMatches); // Test the interfaces inherited by the base class for (size_t i = 0; i < s->interfaces_dim; ++i) { BaseClass *b = s->interfaces[i]; deduceBaseClassParameters(b, sc, tparam, parameters, dedtypes, best, numBaseClassMatches); } s = ((BaseClass *)(s->baseclasses->data[0]))->base; } if (numBaseClassMatches == 0) return MATCHnomatch; // If we got at least one match, copy the known types into dedtypes memcpy(dedtypes->data, best->data, best->dim * sizeof(void *)); return MATCHconvert; } // Extra check if (tparam && tparam->ty == Tclass) { TypeClass *tp = (TypeClass *)tparam; //printf("\t%d\n", (MATCH) implicitConvTo(tp)); return implicitConvTo(tp); } return Type::deduceType(sc, tparam, parameters, dedtypes); }
Comment #5 by clugdbug — 2010-09-03T11:24:34Z
Created attachment 745 Template Interface tests for test suite These 16 tests are for both D1 and D2. Compile with dmd -c templiface.d
Comment #6 by bugzilla — 2010-09-11T17:51:22Z