Skip to content

Commit

Permalink
Merge pull request #4814 from 9rnsr/fix14541
Browse files Browse the repository at this point in the history
Issue 14541 - "duplicate COMDAT" linker error with the template forward reference in Tuple.opAssign
  • Loading branch information
rainers committed Jul 19, 2015
2 parents f78b25c + 1112eec commit 0b0d373
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/template.c
Original file line number Diff line number Diff line change
Expand Up @@ -5925,6 +5925,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
* implements the typeargs. If so, just refer to that one instead.
*/
inst = tempdecl->findExistingInstance(this, fargs);
TemplateInstance *errinst = NULL;
if (!inst)
{
// So, we need to implement 'this' instance.
Expand All @@ -5933,6 +5934,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
{
// If the first instantiation had failed, re-run semantic,
// so that error messages are shown.
errinst = inst;
}
else
{
Expand Down Expand Up @@ -6333,6 +6335,27 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
symtab = NULL;
}
}
else if (errinst)
{
/* Bugzilla 14541: If the previous gagged instance had failed by
* circular references, currrent "error reproduction instantiation"
* might succeed, because of the difference of instantiated context.
* On such case, the cached error instance needs to be overridden by the
* succeeded instance.
*/
size_t bi = hash % tempdecl->buckets.dim;
TemplateInstances *instances = tempdecl->buckets[bi];
assert(instances);
for (size_t i = 0; i < instances->dim; i++)
{
TemplateInstance *ti = (*instances)[i];
if (ti == errinst)
{
(*instances)[i] = this; // override
break;
}
}
}

#if LOG
printf("-TemplateInstance::semantic('%s', this=%p)\n", toChars(), this);
Expand Down
54 changes: 54 additions & 0 deletions test/runnable/imports/link14541traits.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module imports.link14541traits;

template hasElaborateAssign(S)
{
static if (is(S == struct))
{
extern __gshared S lvalue;

enum hasElaborateAssign = is(typeof(S.init.opAssign(S.init))) ||
is(typeof(S.init.opAssign(lvalue)));
}
else
{
enum bool hasElaborateAssign = false;
}
}

void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc
{
static if (hasElaborateAssign!T)
{
}
else
{
}
}

template Tuple(Types...)
{
struct Tuple
{
Types field;
alias field this;

this(Types values)
{
field[] = values[];
}

void opAssign(R)(auto ref R rhs)
{
static if (is(R : Tuple!Types) && !__traits(isRef, rhs))
{
// Use swap-and-destroy to optimize rvalue assignment
swap!(Tuple!Types)(this, rhs);
}
else
{
// Do not swap; opAssign should be called on the fields.
field[] = rhs.field[];
}
}
}
}
42 changes: 42 additions & 0 deletions test/runnable/link14541.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import imports.link14541traits;

void main()
{
Tuple!(int, int) result;

alias T = typeof(result);
static assert(hasElaborateAssign!T);
// hasElaborateAssign!(Tuple(int, int)):
// 1. instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue]
// 2. instantiates swap!(Tuple!(int, int))
// 3. instantiates hasElaborateAssign!(Tuple!(int, int))
// --> forward reference error
// --> swap!(Tuple!(int, int)) fails to instantiate
// --> Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = rvalue] fails to instantiate
// 4. instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Lvalue]
// --> succeeds
// hasElaborateAssign!(Tuple(int, int)) succeeds to instantiate (result is 'true')

// Instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue], but
// it's already done in gagged context, so this is made an error reproduction instantiation.
// But, the forward reference of hasElaborateAssign!(Tuple(int, int)) is already resolved, so
// the instantiation will succeeds.
result = Tuple!(int, int)(0, 0); // --> 1st error reproduction instantiation
result = Tuple!(int, int)(0, 0); // --> 2nd error reproduction instantiation

// The two error reproduction instantiations generate the function:
// Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue]
// twice, then it will cause duplicate COMDAT error in Win64 platform.
}

/+
The point is, if instantiated contexts are different, two instantiations may cause different result.
- The 1st Tuple.opAssign instantiation is invoked from hasElaborateAssign template with gagging.
So it has failed, because of the circular reference of hasElaborateAssign template..
- The 2nd Tuple.opAssign instantiation is invoked from main() without gagging.
It does not have circular reference, so the instantiation should succeed.
Therefore, the gagged failure should be overridden by the ungagged success.
+/

0 comments on commit 0b0d373

Please sign in to comment.