Thread overview
[Issue 4173] New: Explicitly instantiated templates still try to do IFTI in some cases
[Issue 4173] Regression(2.037) Explicitly instantiated templates still try to do IFTI in some cases
Jul 26, 2010
Don
Aug 30, 2010
Don
Sep 03, 2010
Don
Sep 03, 2010
Don
Sep 03, 2010
Don
Sep 12, 2010
Walter Bright
May 10, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4173

           Summary: Explicitly instantiated templates still try to do IFTI
                    in some cases
           Product: D
           Version: 2.037
          Platform: Other
        OS/Version: Linux
            Status: NEW
          Severity: regression
          Priority: P2
         Component: DMD
        AssignedTo: nobody@puremagic.com
        ReportedBy: schveiguy@yahoo.com


--- Comment #0 from Steven Schveighoffer <schveiguy@yahoo.com> 2010-05-10 14:17:02 PDT ---
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.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
July 26, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4173


Don <clugdbug@yahoo.com.au> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
            Summary|Explicitly instantiated     |Regression(2.037)
                   |templates still try to do   |Explicitly instantiated
                   |IFTI in some cases          |templates still try to do
                   |                            |IFTI in some cases


--- Comment #1 from Don <clugdbug@yahoo.com.au> 2010-07-26 11:33:55 PDT ---
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);
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 30, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4173



--- Comment #2 from Don <clugdbug@yahoo.com.au> 2010-08-30 13:52:14 PDT ---
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.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
September 03, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4173



--- Comment #3 from Don <clugdbug@yahoo.com.au> 2010-09-03 01:45:03 PDT ---
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.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
September 03, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4173


Don <clugdbug@yahoo.com.au> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |patch


--- Comment #4 from Don <clugdbug@yahoo.com.au> 2010-09-03 11:19:35 PDT ---
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);
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
September 03, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4173



--- Comment #5 from Don <clugdbug@yahoo.com.au> 2010-09-03 11:24:34 PDT ---
Created an attachment (id=745)
Template Interface tests for test suite

These 16 tests are for both D1 and D2. Compile with dmd -c templiface.d

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
September 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4173


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |bugzilla@digitalmars.com
         Resolution|                            |FIXED


--- Comment #6 from Walter Bright <bugzilla@digitalmars.com> 2010-09-11 17:51:22 PDT ---
http://www.dsource.org/projects/dmd/changeset/675

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------