Skip to content

Commit

Permalink
Merge pull request #2421 from 9rnsr/fix10726
Browse files Browse the repository at this point in the history
[REG2.064a] Issue 10726 - Bogus Circular Reference error if opEquals defined and has a loop
  • Loading branch information
rainers committed Aug 12, 2013
2 parents e615dbf + 6fc381e commit 4e9a7b1
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 102 deletions.
89 changes: 36 additions & 53 deletions src/clone.c
Expand Up @@ -507,6 +507,23 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc)
}
}

if (!xerreq)
{
Identifier *id = Lexer::idPool("_xopEquals");
Expression *e = new IdentifierExp(loc, Id::empty);
e = new DotIdExp(loc, e, Id::object);
e = new DotIdExp(loc, e, id);
e = e->semantic(sc);
Dsymbol *s = getDsymbol(e);
if (!s)
{
::error(Loc(), "ICE: %s not found in object module. You must update druntime", id->toChars());
fatal();
}
assert(s);
xerreq = s->isFuncDeclaration();
}

Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly
Loc loc = Loc(); // loc is unnecessary so errors are gagged

Expand All @@ -525,43 +542,17 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc)

fop->fbody = new ReturnStatement(loc, e);

size_t index = members->dim;
members->push(fop);

unsigned errors = global.startGagging(); // Do not report errors, even if the
unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it.
global.speculativeGag = global.gag;
unsigned errors = global.startGagging(); // Do not report errors
Scope *sc2 = sc->push();
sc2->stc = 0;
sc2->linkage = LINKd;
sc2->speculative = true;

fop->semantic(sc2);
fop->semantic2(sc2);
fop->semantic3(sc2);

sc2->pop();
global.speculativeGag = oldspec;
if (global.endGagging(errors)) // if errors happened
{
members->remove(index);

if (!xerreq)
{
Expression *e = new IdentifierExp(loc, Id::empty);
e = new DotIdExp(loc, e, Id::object);
e = new DotIdExp(loc, e, Lexer::idPool("_xopEquals"));
e = e->semantic(sc);
Dsymbol *s = getDsymbol(e);
assert(s);
FuncDeclaration *fd = s->isFuncDeclaration();

xerreq = fd;
}
fop = xerreq;
}
else
fop->addMember(sc, this, 1);

return fop;
}
Expand Down Expand Up @@ -642,6 +633,23 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc)
#endif
}

if (!xerrcmp)
{
Identifier *id = Lexer::idPool("_xopCmp");
Expression *e = new IdentifierExp(loc, Id::empty);
e = new DotIdExp(loc, e, Id::object);
e = new DotIdExp(loc, e, id);
e = e->semantic(sc);
Dsymbol *s = getDsymbol(e);
if (!s)
{
::error(Loc(), "ICE: %s not found in object module. You must update druntime", id->toChars());
fatal();
}
assert(s);
xerrcmp = s->isFuncDeclaration();
}

Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly
Loc loc = Loc(); // loc is unnecessary so errors are gagged

Expand All @@ -660,42 +668,17 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc)

fop->fbody = new ReturnStatement(loc, e);

size_t index = members->dim;
members->push(fop);

unsigned errors = global.startGagging(); // Do not report errors, even if the
unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it.
global.speculativeGag = global.gag;
unsigned errors = global.startGagging(); // Do not report errors
Scope *sc2 = sc->push();
sc2->stc = 0;
sc2->linkage = LINKd;
sc2->speculative = true;

fop->semantic(sc2);
fop->semantic2(sc2);
fop->semantic3(sc2);

sc2->pop();
global.speculativeGag = oldspec;
if (global.endGagging(errors)) // if errors happened
{
members->remove(index);
if (!xerrcmp)
{
Expression *e = new IdentifierExp(loc, Id::empty);
e = new DotIdExp(loc, e, Id::object);
e = new DotIdExp(loc, e, Lexer::idPool("_xopCmp"));
e = e->semantic(sc);
Dsymbol *s = getDsymbol(e);
assert(s);
FuncDeclaration *fd = s->isFuncDeclaration();

xerrcmp = fd;
}
fop = xerrcmp;
}
else
fop->addMember(sc, this, 1);

return fop;
}
Expand Down
75 changes: 58 additions & 17 deletions src/struct.c
Expand Up @@ -24,6 +24,25 @@
FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals
FuncDeclaration *StructDeclaration::xerrcmp; // object.xopCmp

bool isImportedSym(Dsymbol *s)
{
if (!s || !s->parent)
return false;
s = s->parent;
for (; s; s = s->parent)
{
if (s->isTemplateInstance())
return false;
else if (Module *m = s->isModule())
{
if (m->importedFrom != m)
return true;
break;
}
}
return false;
}

/********************************* AggregateDeclaration ****************************/

AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id)
Expand Down Expand Up @@ -89,22 +108,6 @@ void AggregateDeclaration::semantic2(Scope *sc)
//printf("\t[%d] %s\n", i, s->toChars());
s->semantic2(sc);
}

if (StructDeclaration *sd = isStructDeclaration())
{
/* Even if the struct exists in imported module, calculating
* xeq and xcmp is necessary in order to generate correct TypeInfo.
* However, immediately doing it at the end of StructDeclaration::semantic
* might cause forward reference error during instantiation of
* template opEquals/opCmp. So should be done at the end of semantic2.
*/
//if (sd->xeq != NULL) printf("sd = %s xeq @ [%s]\n", sd->toChars(), sd->loc.toChars());
//assert(sd->xeq == NULL);
if (sd->xeq == NULL)
sd->xeq = sd->buildXopEquals(sc);
if (sd->xcmp == NULL)
sd->xcmp = sd->buildXopCmp(sc);
}
sc->pop();
}
}
Expand All @@ -114,6 +117,10 @@ void AggregateDeclaration::semantic3(Scope *sc)
//printf("AggregateDeclaration::semantic3(%s)\n", toChars());
if (members)
{
StructDeclaration *sd = isStructDeclaration();
if (isImportedSym(sd))
goto Lxop;

sc = sc->push(this);
sc->parent = this;
for (size_t i = 0; i < members->dim; i++)
Expand All @@ -126,7 +133,8 @@ void AggregateDeclaration::semantic3(Scope *sc)
if (!getRTInfo && Type::rtinfo &&
(!isDeprecated() || global.params.useDeprecated) && // don't do it for unused deprecated types
(type && type->ty != Terror)) // or error types
{ // Evaluate: gcinfo!type
{
// Evaluate: RTinfo!type
Objects *tiargs = new Objects();
tiargs->push(type);
TemplateInstance *ti = new TemplateInstance(loc, Type::rtinfo, tiargs);
Expand All @@ -143,6 +151,29 @@ void AggregateDeclaration::semantic3(Scope *sc)
e = e->ctfeInterpret();
getRTInfo = e;
}

if (sd)
{
Lxop:
if (sd->xeq &&
sd->xeq->scope &&
sd->xeq->semanticRun < PASSsemantic3done)
{
unsigned errors = global.startGagging();
sd->xeq->semantic3(sd->xeq->scope);
if (global.endGagging(errors))
sd->xeq = sd->xerreq;
}
if (sd->xcmp &&
sd->xcmp->scope &&
sd->xcmp->semanticRun < PASSsemantic3done)
{
unsigned errors = global.startGagging();
sd->xcmp->semantic3(sd->xcmp->scope);
if (global.endGagging(errors))
sd->xcmp = sd->xerrcmp;
}
}
}
}

Expand Down Expand Up @@ -701,6 +732,16 @@ void StructDeclaration::semantic(Scope *sc)

buildOpAssign(sc2);
buildOpEquals(sc2);

xeq = buildXopEquals(sc2);
xcmp = buildXopCmp(sc2);

/* Even if the struct is merely imported and its semantic3 is not run,
* the TypeInfo object would be speculatively stored in each object
* files. To set correct function pointer, run semantic3 for xeq and xcmp.
*/
if ((xeq && xeq != xerreq || xcmp && xcmp != xerrcmp) && isImportedSym(this))
Module::addDeferredSemantic3(this);
#endif
inv = buildInv(sc2);

Expand Down
5 changes: 3 additions & 2 deletions src/template.c
Expand Up @@ -5463,8 +5463,9 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
#endif

//if (scx && scx->scopesym) printf("3: scx is %s %s\n", scx->scopesym->kind(), scx->scopesym->toChars());
if (scx && scx->scopesym &&
scx->scopesym->members && !scx->scopesym->isTemplateMixin()
if (scx && scx->scopesym && scx->scopesym->members &&
!scx->scopesym->isTemplateMixin() &&
!scx->scopesym->isModule()
#if 0 // removed because it bloated compile times
/* The problem is if A imports B, and B imports A, and both A
* and B instantiate the same template, does the compilation of A
Expand Down
5 changes: 5 additions & 0 deletions src/toobj.c
Expand Up @@ -986,6 +986,11 @@ void StructDeclaration::toObjFile(int multiobj)
*/
member->toObjFile(multiobj);
}

if (xeq && xeq != xerreq)
xeq->toObjFile(multiobj);
if (xcmp && xcmp != xerrcmp)
xcmp->toObjFile(multiobj);
}
}

Expand Down
24 changes: 0 additions & 24 deletions test/compilable/extra-files/json.out
Expand Up @@ -268,30 +268,6 @@
"originalType" : "Object",
}
]
},
{
"name" : "__xopEquals",
"kind" : "function",
"storageClass" : [
"static"
],
"deco" : "FKxS4json4Foo2KxS4json4Foo2Zb",
"parameters" : [
{
"name" : "p",
"deco" : "xS4json4Foo2",
"storageClass" : [
"ref"
]
},
{
"name" : "q",
"deco" : "xS4json4Foo2",
"storageClass" : [
"ref"
]
}
]
}
]
},
Expand Down
53 changes: 53 additions & 0 deletions test/compilable/test10726.d
@@ -0,0 +1,53 @@
// PERMUTE_ARGS:

public struct CirBuff(T)
{
private T[] data;
private size_t head = 0;
private size_t size = 0;
public size_t length() const { return size; }

public bool opEquals(CirBuff!T d) @trusted
{
if (length != d.length)
return false;
for (size_t i=0; i!=size; ++i)
{
if (this.data[(this.head+i) % this.data.length] !=
d.data[(d.head + i) % d.data.length])
{
return false;
}
}
return true;
}
}

class Once
{
Foo!Bar _bar;
}

class Bar
{
static Once _once;
mixin(sync!(Once, "_once"));
}

class Foo(T = int)
{
CirBuff!T _buff;
}

template sync(T, string U = "this", size_t ITER = 0)
{
static if (ITER == __traits(derivedMembers, T).length)
enum sync = "";
else
{
enum string mem = __traits(derivedMembers, T)[ITER];
enum string sync =
"static if(! __traits(isVirtualMethod, " ~ U ~ "." ~ mem ~ ")) { }"
~ sync!(T, U, ITER+1);
}
}
12 changes: 9 additions & 3 deletions test/runnable/extra-files/test10567.d
@@ -1,9 +1,15 @@
import test10567a;

template TypeTuple(TL...) { alias TL TypeTuple; }

void main()
{
auto i = BigInt([100]);
auto j = BigInt([100]);
foreach (BigInt; TypeTuple!(BigInt1, BigInt2, BigInt3))
{
auto i = BigInt([100]);
auto j = BigInt([100]);

assert(typeid(BigInt).compare(&i, &j) == 0);
assert(typeid(BigInt).equals(&i, &j) == true);
assert(typeid(BigInt).compare(&i, &j) == 0);
}
}

0 comments on commit 4e9a7b1

Please sign in to comment.