102 changes: 60 additions & 42 deletions gcc/d/dfrontend/expression.c
Original file line number Diff line number Diff line change
Expand Up @@ -8402,6 +8402,37 @@ Expression *CallExp::syntaxCopy()
return new CallExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
}

static FuncDeclaration *resolveOverloadSet(Loc loc, Scope *sc,
OverloadSet *os, Objects* tiargs, Type *tthis, Expressions *arguments)
{
FuncDeclaration *f = NULL;
for (size_t i = 0; i < os->a.dim; i++)
{
Dsymbol *s = os->a[i];
if (tiargs && s->isFuncDeclaration())
continue;
if (FuncDeclaration *f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, 1))
{
if (f2->errors)
return NULL;
if (f)
{
/* Error if match in more than one overload set,
* even if one is a 'better' match than the other.
*/
ScopeDsymbol::multiplyDefined(loc, f, f2);
}
else
f = f2;
}
}
if (!f)
::error(loc, "no overload matches for %s", os->toChars());
else if (f->errors)
f = NULL;
return f;
}

Expression *CallExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
Expand Down Expand Up @@ -8799,7 +8830,20 @@ Expression *CallExp::semantic(Scope *sc)
f = resolveFuncCall(loc, sc, s, tiargs, ue1 ? ue1->type : NULL, arguments);
if (!f || f->errors || f->type->ty == Terror)
return new ErrorExp();

if (f->interfaceVirtual)
{
/* Cast 'this' to the type of the interface, and replace f with the interface's equivalent
*/
BaseClass *b = f->interfaceVirtual;
ClassDeclaration *ad2 = b->sym;
ue->e1 = ue->e1->castTo(sc, ad2->type->addMod(ue->e1->type->mod));
ue->e1 = ue->e1->semantic(sc);
ue1 = ue->e1;
int vi = f->findVtblIndex((Dsymbols*)&ad2->vtbl, (int)ad2->vtbl.dim);
assert(vi >= 0);
f = ad2->vtbl[vi]->isFuncDeclaration();
assert(f);
}
if (f->needThis())
{
AggregateDeclaration *ad = f->toParent2()->isAggregateDeclaration();
Expand Down Expand Up @@ -8888,10 +8932,8 @@ Expression *CallExp::semantic(Scope *sc)
else if (e1->op == TOKsuper)
{
// Base class constructor call
ClassDeclaration *cd = NULL;

if (sc->func && sc->func->isThis())
cd = sc->func->isThis()->isClassDeclaration();
AggregateDeclaration *ad = sc->func ? sc->func->isThis() : NULL;
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
if (!cd || !cd->baseClass || !sc->func->isCtorDeclaration())
{
error("super class constructor call must be in a constructor");
Expand All @@ -8915,7 +8957,10 @@ Expression *CallExp::semantic(Scope *sc)
}

tthis = cd->type->addMod(sc->func->type->mod);
f = resolveFuncCall(loc, sc, cd->baseClass->ctor, NULL, tthis, arguments, 0);
if (OverloadSet *os = cd->baseClass->ctor->isOverloadSet())
f = resolveOverloadSet(loc, sc, os, NULL, tthis, arguments);
else
f = resolveFuncCall(loc, sc, cd->baseClass->ctor, NULL, tthis, arguments, 0);
if (!f || f->errors)
return new ErrorExp();
checkDeprecated(sc, f);
Expand All @@ -8931,11 +8976,8 @@ Expression *CallExp::semantic(Scope *sc)
else if (e1->op == TOKthis)
{
// same class constructor call
AggregateDeclaration *cd = NULL;

if (sc->func && sc->func->isThis())
cd = sc->func->isThis()->isAggregateDeclaration();
if (!cd || !sc->func->isCtorDeclaration())
AggregateDeclaration *ad = sc->func ? sc->func->isThis() : NULL;
if (!ad || !sc->func->isCtorDeclaration())
{
error("constructor call must be in a constructor");
return new ErrorExp();
Expand All @@ -8952,8 +8994,11 @@ Expression *CallExp::semantic(Scope *sc)
sc->callSuper |= CSXany_ctor | CSXthis_ctor;
}

tthis = cd->type->addMod(sc->func->type->mod);
f = resolveFuncCall(loc, sc, cd->ctor, NULL, tthis, arguments, 0);
tthis = ad->type->addMod(sc->func->type->mod);
if (OverloadSet *os = ad->ctor->isOverloadSet())
f = resolveOverloadSet(loc, sc, os, NULL, tthis, arguments);
else
f = resolveFuncCall(loc, sc, ad->ctor, NULL, tthis, arguments, 0);
if (!f || f->errors)
return new ErrorExp();
checkDeprecated(sc, f);
Expand All @@ -8976,37 +9021,10 @@ Expression *CallExp::semantic(Scope *sc)
}
else if (e1->op == TOKoverloadset)
{
OverExp *eo = (OverExp *)e1;
FuncDeclaration *f = NULL;
Dsymbol *s = NULL;
for (size_t i = 0; i < eo->vars->a.dim; i++)
{
s = eo->vars->a[i];
if (tiargs && s->isFuncDeclaration())
continue;
FuncDeclaration *f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, 1);
if (f2)
{
if (f2->errors)
return new ErrorExp();
if (f)
{
/* Error if match in more than one overload set,
* even if one is a 'better' match than the other.
*/
ScopeDsymbol::multiplyDefined(loc, f, f2);
}
else
f = f2;
}
}
OverloadSet *os = ((OverExp *)e1)->vars;
f = resolveOverloadSet(loc, sc, os, tiargs, tthis, arguments);
if (!f)
{
/* No overload matches
*/
error("no overload matches for %s", s->toChars());
return new ErrorExp();
}
if (ethis)
e1 = new DotVarExp(loc, ethis, f);
else
Expand Down
23 changes: 22 additions & 1 deletion gcc/d/dfrontend/func.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla
isArrayOp = 0;
semantic3Errors = false;
fes = NULL;
interfaceVirtual = NULL;
introducing = 0;
tintro = NULL;
/* The type given for "infer the return type" is a TypeFunction with
Expand Down Expand Up @@ -806,7 +807,26 @@ void FuncDeclaration::semantic(Scope *sc)
}
else
{
//printf("\tintroducing function\n");
if (global.params.mscoff && cd->cpp)
{
/* if overriding an interface function, then this is not
* introducing and don't put it in the class vtbl[]
*/
for (size_t i = 0; i < cd->interfaces_dim; i++)
{
BaseClass* b = cd->interfaces[i];
int v = findVtblIndex((Dsymbols*)&b->sym->vtbl, (int)b->sym->vtbl.dim);
if (v >= 0)
{
//printf("\tinterface function %s\n", toChars());
cd->vtblFinal.push(this);
interfaceVirtual = b;
goto Linterfaces;
}
}
}

//printf("\tintroducing function %s\n", toChars());
introducing = 1;
if (cd->cpp && Target::reverseCppOverloads)
{
Expand Down Expand Up @@ -929,6 +949,7 @@ void FuncDeclaration::semantic(Scope *sc)
* If this function is covariant with any members of those interface
* functions, set the tintro.
*/
Linterfaces:
for (size_t i = 0; i < cd->interfaces_dim; i++)
{
BaseClass *b = cd->interfaces[i];
Expand Down
47 changes: 47 additions & 0 deletions gcc/testsuite/gdc.test/compilable/test15784.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// PERMUTE_ARGS:

template AddField(T)
{
T b;
this(Args...)(T b, auto ref Args args)
{
this.b = b;
this(args);
}
}

template construcotrs()
{
int a;
this(int a)
{
this.a = a;
}
}

class B
{
mixin construcotrs;
mixin AddField!(string);
}

class C : B
{
this(A...)(A args)
{
// The called super ctor is an overload set.
super(args);
}
}

struct S
{
mixin construcotrs;
mixin AddField!(string);
}

void main()
{
auto s = S("bar", 15);
auto c = new C("bar", 15);
}
66 changes: 66 additions & 0 deletions gcc/testsuite/gdc.test/runnable/cppa.d
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,71 @@ void test14200()
test14200b(1.0f, 1, 1.0);
}

/****************************************/
// 15579

extern (C++)
{
class Base
{
//~this() {}
void based() { }
ubyte x = 4;
}

interface Interface
{
int MethodCPP();
int MethodD();
}

class Derived : Base, Interface
{
short y = 5;

This comment has been minimized.

Copy link
@jpf91

jpf91 Mar 8, 2017

Contributor

@ibuclaw This can't be supported in GCC <= 4.9, correct?

This test is the only failing test on the GCC 4.9 backport. The thunk for this function is not emitted as weak/comdat in these older GCCs. In newer GCCs finish_thunk expands thunks in gimple so I guess this is a requirement for this testcase to work?

I guess it's basically the same problem as #97 (comment) ?

This comment has been minimized.

Copy link
@ibuclaw

ibuclaw Mar 8, 2017

Author Member

Yeah... you could always try and see if the old way of generating functions works - it should only fail if you are poisoned from including a needed backend header.

(Quick copy-paste from 4.6 or 4.7)

if (targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
                                         virtual_value, alias))
  {
    const char *fnname;
    tree fn_block;
    
    current_function_decl = thunk_fndecl;
    DECL_RESULT (thunk_fndecl)
     = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
                   RESULT_DECL, 0, integer_type_node);
    fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
    /* The back end expects DECL_INITIAL to contain a BLOCK, so we
      create one.  */
    fn_block = make_node (BLOCK);
    BLOCK_VARS (fn_block) = a;
    DECL_INITIAL (thunk_fndecl) = fn_block;
    init_function_start (thunk_fndecl);
    cfun->is_thunk = 1;
    assemble_start_function (thunk_fndecl, fnname);

    targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
                                    fixed_offset, virtual_value, alias);

    assemble_end_function (thunk_fndecl, fnname);
    init_insn_lengths ();
    free_after_compilation (cfun);
    current_function_decl = 0;
    set_cfun (NULL);
    TREE_ASM_WRITTEN (thunk_fndecl) = 1;
  }

This comment has been minimized.

Copy link
@jpf91

jpf91 Mar 9, 2017

Contributor

This is from cgraphunit.c: expand_thunk ? In GCC 4.9 the expand_thunk implementation uses functions from rtl.h which can't be included.

If I simply use your code above the backend fails in cgraphunit.c: decide_is_symbol_needed:

  /* Double check that no one output the function into assembly file
     early.  */
  gcc_checking_assert (!DECL_ASSEMBLER_NAME_SET_P (decl)
	               || !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));

I guess I'd have to look into thunk handling in GCC in more detail to finally understand what this code is actually doing ;-) But right now it looks like this workaround won't work either.

This comment has been minimized.

Copy link
@ibuclaw

ibuclaw Mar 10, 2017

Author Member

Haha.

This comment has been minimized.

Copy link
@ibuclaw

ibuclaw Mar 10, 2017

Author Member

I guess your choices are:

  1. Backport force_gimple_thunk to earlier versions, benefiting gcc-4.9, 4.8, and earlier...
  2. Generate the thunk in the frontend, which may benefit all versions - in particular there is no support for variadic thunks that must fallback on the generic version.

The body should look like this:

_DTsomeFunc(this, args)
{
  this += offset;
  someFunc(this, args);
}

The tricky bit is making sure that args are passed down correctly from va_args.

int MethodCPP();
int MethodD() {
printf("Derived.MethodD(): this = %p, x = %d, y = %d\n", this, x, y);
Derived p = this;
//p = cast(Derived)(cast(void*)p - 16);
assert(p.x == 4 || p.x == 7);
assert(p.y == 5 || p.y == 8);
return 3;
}
int Method() { return 6; }
}

Derived cppfoo(Derived);
Interface cppfooi(Interface);
}

void test15579()
{
Derived d = new Derived();
printf("d = %p\n", d);
assert(d.x == 4);
assert(d.y == 5);
assert((cast(Interface)d).MethodCPP() == 30);
assert((cast(Interface)d).MethodD() == 3);
assert(d.MethodCPP() == 30);
assert(d.MethodD() == 3);
assert(d.Method() == 6);

d = cppfoo(d);
assert(d.x == 7);
assert(d.y == 8);

printf("d2 = %p\n", d);
assert((cast(Interface)d).MethodD() == 3);
assert((cast(Interface)d).MethodCPP() == 30);
assert(d.Method() == 6);

printf("d = %p, i = %p\n", d, cast(Interface)d);
Interface i = cppfooi(d);
printf("i2: %p\n", i);
assert(i.MethodD() == 3);
assert(i.MethodCPP() == 30);
}

/****************************************/

void main()
Expand Down Expand Up @@ -794,6 +859,7 @@ void main()
foo13337(S13337());
test14195();
test14200();
test15579();

printf("Success\n");
}
75 changes: 75 additions & 0 deletions gcc/testsuite/gdc.test/runnable/extra-files/cppb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,3 +512,78 @@ void test14200a(int a) {};
void test14200b(float a, int b, double c) {};

/******************************************/
// 15579

class Base
{
public:
//virtual ~Base() {}
virtual void base();
unsigned char x;
};

class Interface
{
public:
virtual int MethodCPP() = 0;
virtual int MethodD() = 0;
};

class Derived : public Base, public Interface
{
public:
Derived();
short y;
int MethodCPP();
#if _WIN32 || _WIN64
int MethodD();
virtual int Method();
#else
int MethodD() { return 3; } // need def or vtbl[] is not generated
virtual int Method() { return 6; } // need def or vtbl[] is not generated
#endif
};

void Base::base() { }
int Derived::MethodCPP() {
printf("Derived::MethodCPP() this = %p, x = %d, y = %d\n", this, x, y);
assert(x == 4 || x == 7);
assert(y == 5 || y == 8);
return 30;
}
Derived::Derived() { }


Derived *cppfoo(Derived *d)
{
printf("cppfoo(): d = %p\n", d);
assert(d->x == 4);
assert(d->y == 5);
assert(d->MethodD() == 3);
assert(d->MethodCPP() == 30);
assert(d->Method() == 6);

d = new Derived();
d->x = 7;
d->y = 8;
assert(d->MethodD() == 3);
assert(d->MethodCPP() == 30);
assert(d->Method() == 6);
printf("d1 = %p\n", d);
return d;
}

Interface *cppfooi(Interface *i)
{
printf("cppfooi(): i = %p\n", i);
assert(i->MethodD() == 3);
assert(i->MethodCPP() == 30);

Derived *d = new Derived();
d->x = 7;
d->y = 8;
printf("d = %p, i = %p\n", d, (Interface *)d);
return d;
}

/******************************************/