Skip to content

Commit

Permalink
Merge pull request #3710 from 9rnsr/fix_hash
Browse files Browse the repository at this point in the history
Introduce correct default hash calculation that is consistent with object equality
  • Loading branch information
WalterBright committed Jul 6, 2014
2 parents 49bc16e + a5c8c3a commit a2f9ddf
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 37 deletions.
2 changes: 2 additions & 0 deletions src/aggregate.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ bool needOpEquals(StructDeclaration *sd);
FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildXtoHash(StructDeclaration *ad, Scope *sc);
FuncDeclaration *buildCpCtor(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc);
Expand Down Expand Up @@ -151,6 +152,7 @@ class StructDeclaration : public AggregateDeclaration

FuncDeclaration *xeq; // TypeInfo_Struct.xopEquals
FuncDeclaration *xcmp; // TypeInfo_Struct.xopCmp
FuncDeclaration *xhash; // TypeInfo_Struct.xtoHash
static FuncDeclaration *xerreq; // object.xopEquals
static FuncDeclaration *xerrcmp; // object.xopCmp

Expand Down
110 changes: 110 additions & 0 deletions src/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,116 @@ FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc)
return fop;
}

/*******************************************
* We need a toHash for the struct if
* any fields has a toHash.
* Generate one if a user-specified one does not exist.
*/

bool needToHash(StructDeclaration *sd)
{
//printf("StructDeclaration::needToHash() %s\n", sd->toChars());

if (sd->xhash)
goto Lneed;

if (sd->isUnionDeclaration())
goto Ldontneed;

/* If any of the fields has an opEquals, then we
* need it too.
*/
for (size_t i = 0; i < sd->fields.dim; i++)
{
VarDeclaration *v = sd->fields[i];
if (v->storage_class & STCref)
continue;
Type *tv = v->type->toBasetype();
if (tv->isfloating())
goto Lneed;
if (tv->ty == Tarray)
goto Lneed;
if (tv->ty == Taarray)
goto Lneed;
if (tv->ty == Tclass)
goto Lneed;
tv = tv->baseElemOf();
if (tv->ty == Tstruct)
{
TypeStruct *ts = (TypeStruct *)tv;
if (needToHash(ts->sym))
goto Lneed;
}
}
Ldontneed:
//printf("\tdontneed\n");
return false;

Lneed:
//printf("\tneed\n");
return true;
}

/******************************************
* Build __xtoHash for non-bitwise hashing
* static hash_t xtoHash(ref const S p) nothrow @trusted;
*/

FuncDeclaration *buildXtoHash(StructDeclaration *sd, Scope *sc)
{
if (Dsymbol *s = search_function(sd, Id::tohash))
{
static TypeFunction *tftohash;
if (!tftohash)
{
tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd);
tftohash->mod = MODconst;
tftohash = (TypeFunction *)tftohash->merge();
}

if (FuncDeclaration *fd = s->isFuncDeclaration())
{
fd = fd->overloadExactMatch(tftohash);
if (fd)
return fd;
}
}

if (!needToHash(sd))
return NULL;

//printf("StructDeclaration::buildXtoHash() %s\n", sd->toPrettyChars());
Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly
Loc loc = Loc(); // internal code should have no loc to prevent coverage

Parameters *parameters = new Parameters();
parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL));
TypeFunction *tf = new TypeFunction(parameters, Type::thash_t, 0, LINKd, STCnothrow | STCtrusted);
tf = (TypeFunction *)tf->semantic(loc, sc);

Identifier *id = Id::xtoHash;
FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf);

const char *code =
"size_t h = 0;"\
"foreach (i, T; typeof(p.tupleof))"\
" h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);"\
"return h;";
fop->fbody = new CompileStatement(loc, new StringExp(loc, (char *)code));

Scope *sc2 = sc->push();
sc2->stc = 0;
sc2->linkage = LINKd;

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

sc2->pop();

//printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars());
return fop;
}

/*******************************************
* Build copy constructor for struct.
* void __cpctpr(ref const S s) [pure nothrow @trusted]
Expand Down
1 change: 1 addition & 0 deletions src/idgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Msgtable msgtable[] =
{ "postblit" },
{ "xopEquals", "__xopEquals" },
{ "xopCmp", "__xopCmp" },
{ "xtoHash", "__xtoHash" },

{ "LINE", "__LINE__" },
{ "FILE", "__FILE__" },
Expand Down
36 changes: 7 additions & 29 deletions src/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,6 @@ TypeTuple *toArgTypes(Type *t);
FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals
FuncDeclaration *StructDeclaration::xerrcmp; // object.xopCmp

/***************************************
* Search toHash member function for TypeInfo_Struct.
* const hash_t toHash();
*/
FuncDeclaration *search_toHash(StructDeclaration *sd)
{
Dsymbol *s = search_function(sd, Id::tohash);
FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
if (fd)
{
static TypeFunction *tftohash;
if (!tftohash)
{
tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd);
tftohash->mod = MODconst;
tftohash = (TypeFunction *)tftohash->merge();
}

fd = fd->overloadExactMatch(tftohash);
}
return fd;
}

/***************************************
* Search toString member function for TypeInfo_Struct.
* string toString();
Expand Down Expand Up @@ -117,7 +94,7 @@ void semanticTypeInfo(Scope *sc, Type *t)
sd->xcmp && sd->xcmp != sd->xerrcmp ||
(sd->postblit && !(sd->postblit->storage_class & STCdisable)) ||
sd->dtor ||
search_toHash(sd) ||
sd->xhash ||
search_toString(sd)
) &&
sd->inNonRoot())
Expand Down Expand Up @@ -312,12 +289,11 @@ void StructDeclaration::semanticTypeInfoMembers()
ftostr->semantic3(ftostr->scope);
}

FuncDeclaration *ftohash = search_toHash(this);
if (ftohash &&
ftohash->scope &&
ftohash->semanticRun < PASSsemantic3done)
if (xhash &&
xhash->scope &&
xhash->semanticRun < PASSsemantic3done)
{
ftohash->semantic3(ftohash->scope);
xhash->semantic3(xhash->scope);
}

if (postblit &&
Expand Down Expand Up @@ -649,6 +625,7 @@ StructDeclaration::StructDeclaration(Loc loc, Identifier *id)

xeq = NULL;
xcmp = NULL;
xhash = NULL;
alignment = 0;
ispod = ISPODfwd;
arg1type = NULL;
Expand Down Expand Up @@ -856,6 +833,7 @@ void StructDeclaration::semantic(Scope *sc)

xeq = buildXopEquals(this, sc2);
xcmp = buildXopCmp(this, sc2);
xhash = buildXtoHash(this, sc2);

/* Even if the struct is merely imported and its semantic3 is not run,
* the TypeInfo object would be speculatively stored in each object
Expand Down
8 changes: 6 additions & 2 deletions src/toobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -826,12 +826,14 @@ void StructDeclaration::toObjFile(bool multiobj)
//printf("StructDeclaration::toObjFile('%s')\n", toChars());

if (type->ty == Terror)
{ error("had semantic errors when compiling");
{
error("had semantic errors when compiling");
return;
}

if (multiobj && !hasStaticCtorOrDtor())
{ obj_append(this);
{
obj_append(this);
return;
}

Expand Down Expand Up @@ -878,6 +880,8 @@ void StructDeclaration::toObjFile(bool multiobj)
xeq->toObjFile(multiobj);
if (xcmp && xcmp != xerrcmp)
xcmp->toObjFile(multiobj);
if (xhash)
xhash->toObjFile(multiobj);
}
}

Expand Down
19 changes: 13 additions & 6 deletions src/typinf.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ Expression *Type::getInternalTypeInfo(Scope *sc)
}


FuncDeclaration *search_toHash(StructDeclaration *sd);
FuncDeclaration *search_toString(StructDeclaration *sd);

/****************************************************
Expand Down Expand Up @@ -131,11 +130,19 @@ void Type::genTypeInfo(Scope *sc)
// Generate COMDAT
if (sc) // if in semantic() pass
{
// Find module that will go all the way to an object file
Module *m = sc->module->importedFrom;
m->members->push(t->vtinfo);
if (sc->func && !sc->func->isInstantiated() && sc->func->inNonRoot())
{
// Bugzilla 13043: Avoid linking TypeInfo if it's not
// necessary for root module compilation
}
else
{
// Find module that will go all the way to an object file
Module *m = sc->module->importedFrom;
m->members->push(t->vtinfo);

semanticTypeInfo(sc, t);
semanticTypeInfo(sc, t);
}
}
else // if in obj generation pass
{
Expand Down Expand Up @@ -586,7 +593,7 @@ class TypeInfoDtVisitor : public Visitor
else
dtxoff(pdt, sd->toInitializer(), 0); // init.ptr

if (FuncDeclaration *fd = search_toHash(sd))
if (FuncDeclaration *fd = sd->xhash)
{
dtxoff(pdt, toSymbol(fd), 0);
TypeFunction *tf = (TypeFunction *)fd->type;
Expand Down
17 changes: 17 additions & 0 deletions test/runnable/imports/link13043a.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module imports.lin13043a;

struct QualifiedNameTests
{
struct Inner
{
const int opCmp(ref const Inner) { return 0; }
}

shared(const(Inner[string])[]) data;

version(bug)
size_t toHash() const
{
return typeid(typeof(data)).getHash(cast(const void*)&data);
}
}
5 changes: 5 additions & 0 deletions test/runnable/link13043.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// PERMUTE_ARGS: -g -inline -version=bug -release -O

import imports.link13043a;

void main() {}
58 changes: 58 additions & 0 deletions test/runnable/testtypeid.d
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,62 @@ void test11010()
assert(ti.toString() == "testtypeid.D11010");
}

/******************************************************/
// 13045

void test13045a()
{
static struct S
{
int[] a;
}

auto s1 = S([1,2]);
auto s2 = S([1,2]);
assert(s1 !is s2);
assert(s1 == s2);
assert(typeid(S).getHash(&s1) == typeid(S).getHash(&s2)); // should succeed
}

void test13045b()
{
bool thrown(T)(lazy T cond)
{
import core.exception;
try
cond();
catch (Error e)
return true;
return false;
}

struct S
{
size_t toHash() const nothrow @safe
{
// all getHash call should reach here
throw new Error("");
}
}
struct T
{
S s;
}
S s;
assert(thrown(typeid(S).getHash(&s))); // OK
S[1] ssa;
assert(thrown(typeid(S[1]).getHash(&ssa))); // OK
S[] sda = [S(), S()];
assert(thrown(typeid(S[]).getHash(&sda))); // OK

T t;
assert(thrown(typeid(T).getHash(&t))); // OK <- NG
T[1] tsa;
assert(thrown(typeid(T[1]).getHash(&tsa))); // OK <- NG
T[] tda = [T(), T()];
assert(thrown(typeid(T[]).getHash(&tda))); // OK <- NG
}

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

int main()
Expand Down Expand Up @@ -564,6 +620,8 @@ int main()
test9442();
test10451();
test11010();
test13045a();
test13045b();

return 0;
}

0 comments on commit a2f9ddf

Please sign in to comment.