Skip to content

Commit

Permalink
Separate returned value isolation check from purity level calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
9rnsr committed Feb 5, 2013
1 parent 49b4d94 commit f3b5817
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/cast.c
Expand Up @@ -638,7 +638,7 @@ MATCH CallExp::implicitConvTo(Type *t)
/* Allow the result of strongly pure functions to
* convert to immutable
*/
if (f && f->isPure() == PUREstrong && !f->type->hasWild())
if (f && f->isolateReturn())
return type->invariantOf()->implicitConvTo(t);

return MATCHnomatch;
Expand Down
2 changes: 2 additions & 0 deletions src/declaration.h
Expand Up @@ -683,6 +683,8 @@ struct FuncDeclaration : Declaration
bool isSafeBypassingInference();
int isTrusted();
bool setUnsafe();
bool isolateReturn();
bool parametersIntersect(Type *t);
virtual int isNested();
int needThis();
int isVirtualMethod();
Expand Down
162 changes: 162 additions & 0 deletions src/func.c
Expand Up @@ -3120,6 +3120,168 @@ bool FuncDeclaration::setUnsafe()
return FALSE;
}

/**************************************
* Returns an indirect type one step from t.
*/

Type *getIndirection(Type *t)
{
t = t->toBasetype();

if (t->ty == Tsarray)
{ while (t->ty == Tsarray)
t = t->nextOf()->toBasetype();
}
if (t->ty == Tarray || t->ty == Tpointer)
return t->nextOf()->toBasetype();
if (t->ty == Taarray || t->ty == Tclass)
return t;
if (t->ty == Tstruct)
return t->hasPointers() ? t : NULL; // TODO

// should consider TypeDelegate?
return NULL;
}

/**************************************
* Traverse this and t, and then check the indirections convertibility.
*/

int traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool a2b = true)
{
if (a2b) // check ta appears in tb
{
//printf("\ttraverse(1) %s appears in %s\n", ta->toChars(), tb->toChars());
if (ta->constConv(tb))
return 1;
else if (ta->invariantOf()->equals(tb->invariantOf()))
return 0;
}
else // check tb appears in ta
{
//printf("\ttraverse(2) %s appears in %s\n", tb->toChars(), ta->toChars());
if (tb->constConv(ta))
return 1;
else if (tb->invariantOf()->equals(ta->invariantOf()))
return 0;
}

// context date to detect circular look up
struct Ctxt
{
Ctxt *prev;
Type *type;
};
Ctxt *ctxt = (Ctxt *)p;

Type *tbb = tb->toBasetype();
if (tbb != tb)
return traverseIndirections(ta, tbb, ctxt, a2b);

if (tb->ty == Tsarray)
{ while (tb->toBasetype()->ty == Tsarray)
tb = tb->toBasetype()->nextOf();
}
if (tb->ty == Tclass || tb->ty == Tstruct)
{
for (Ctxt *c = ctxt; c; c = c->prev)
if (tb == c->type) return 0;
Ctxt c;
c.prev = ctxt;
c.type = tb;

AggregateDeclaration *sym = tb->toDsymbol(NULL)->isAggregateDeclaration();
for (size_t i = 0; i < sym->fields.dim; i++)
{
VarDeclaration *v = sym->fields[i];
Type *tprmi = v->type->addMod(tb->mod);
if (!(v->storage_class & STCref))
tprmi = getIndirection(tprmi);
if (!tprmi)
continue;

//printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars());
if (traverseIndirections(ta, tprmi, &c, a2b))
return 1;
}
}
else if (tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tpointer)
{
Type *tind = tb->nextOf();
if (traverseIndirections(ta, tind, ctxt, a2b))
return 1;
}
else if (tb->hasPointers())
{
// FIXME: function pointer/delegate types should be considered.
return 1;
}
if (a2b)
return traverseIndirections(tb, ta, ctxt, false);

return 0;
}

/********************************************
* Returns true if the function return value has no indirection
* which comes from the parameters.
*/

bool FuncDeclaration::isolateReturn()
{
assert(type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)type;
assert(tf->next);

Type *treti = tf->next;
treti = tf->isref ? treti : getIndirection(treti);
if (!treti)
return true; // target has no mutable indirection
return parametersIntersect(treti);
}

/********************************************
* Returns true if an object typed t can have indirections
* which come from the parameters.
*/

bool FuncDeclaration::parametersIntersect(Type *t)
{
assert(t);
if (!isPureBypassingInference() || isNested())
return false;

assert(type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)type;

//printf("parametersIntersect(%s) t = %s\n", tf->toChars(), t->toChars());

size_t dim = Parameter::dim(tf->parameters);
for (size_t i = 0; i < dim; i++)
{
Parameter *fparam = Parameter::getNth(tf->parameters, i);
if (!fparam->type)
continue;
Type *tprmi = (fparam->storageClass & (STClazy | STCout | STCref))
? fparam->type : getIndirection(fparam->type);
if (!tprmi)
continue; // there is no mutable indirection

//printf("\t[%d] tprmi = %d %s\n", i, tprmi->ty, tprmi->toChars());
if (traverseIndirections(tprmi, t))
return false;
}
if (AggregateDeclaration *ad = isThis())
{
Type *tthis = ad ? ad->getType()->addMod(tf->mod) : NULL;
//printf("\ttthis = %s\n", tthis->toChars());
if (traverseIndirections(tthis, t))
return false;
}

return true;
}

// Determine if function needs
// a static frame pointer to its lexically enclosing function

Expand Down
10 changes: 6 additions & 4 deletions src/statement.c
Expand Up @@ -3922,11 +3922,13 @@ Statement *ReturnStatement::semantic(Scope *sc)
}
else if (tbret->ty != Tvoid)
{
if (fd->isPureBypassingInference() == PUREstrong &&
!exp->type->implicitConvTo(tret) &&
exp->type->invariantOf()->implicitConvTo(tret))
if (!exp->type->implicitConvTo(tret) &&
fd->parametersIntersect(exp->type))
{
exp = exp->castTo(sc, exp->type->invariantOf());
if (exp->type->invariantOf()->implicitConvTo(tret))
exp = exp->castTo(sc, exp->type->invariantOf());
else if (exp->type->wildOf()->implicitConvTo(tret))
exp = exp->castTo(sc, exp->type->wildOf());
}
if (fd->tintro)
exp = exp->implicitCastTo(sc, fd->type->nextOf());
Expand Down
93 changes: 93 additions & 0 deletions test/fail_compilation/testInference.d
Expand Up @@ -24,3 +24,96 @@ class C8998
return a; // should be error
}
}

/*
TEST_OUTPUT:
---
fail_compilation/testInference.d(39): Error: cannot implicitly convert expression (s) of type const(char[]) to string
fail_compilation/testInference.d(44): Error: cannot implicitly convert expression (a) of type int[] to immutable(int[])
fail_compilation/testInference.d(49): Error: cannot implicitly convert expression (a) of type int[] to immutable(int[])
fail_compilation/testInference.d(54): Error: cannot implicitly convert expression (a) of type int[] to immutable(int[])
---
*/
string foo(in char[] s) pure
{
return s; //
}
immutable(int[]) x1() /*pure*/
{
int[] a = new int[](10);
return a; //
}
immutable(int[]) x2(int len) /*pure*/
{
int[] a = new int[](len);
return a;
}
immutable(int[]) x3(immutable(int[]) org) /*pure*/
{
int[] a = new int[](org.length);
return a; //
}


/*
TEST_OUTPUT:
---
fail_compilation/testInference.d(94): Error: cannot implicitly convert expression (c) of type testInference.C1 to immutable(C1)
fail_compilation/testInference.d(95): Error: cannot implicitly convert expression (c) of type testInference.C1 to immutable(C1)
fail_compilation/testInference.d(96): Error: cannot implicitly convert expression (c) of type testInference.C3 to immutable(C3)
fail_compilation/testInference.d(97): Error: cannot implicitly convert expression (c) of type testInference.C3 to immutable(C3)
fail_compilation/testInference.d(100): Error: undefined identifier X1, did you mean function x1?
fail_compilation/testInference.d(106): Error: cannot implicitly convert expression (s) of type S1 to immutable(S1)
fail_compilation/testInference.d(109): Error: cannot implicitly convert expression (a) of type int*[] to immutable(int*[])
fail_compilation/testInference.d(110): Error: cannot implicitly convert expression (a) of type const(int)*[] to immutable(int*[])
fail_compilation/testInference.d(114): Error: cannot implicitly convert expression (s) of type S2 to immutable(S2)
fail_compilation/testInference.d(115): Error: cannot implicitly convert expression (s) of type S2 to immutable(S2)
fail_compilation/testInference.d(116): Error: cannot implicitly convert expression (s) of type S2 to immutable(S2)
fail_compilation/testInference.d(118): Error: cannot implicitly convert expression (a) of type const(int)*[] to immutable(int*[])
---
*/
immutable(Object) get(inout int*) pure
{
auto o = new Object;
return o; // should be ok
}
immutable(Object) get(immutable Object) pure
{
auto o = new Object;
return o; // should be ok
}
inout(Object) get(inout Object) pure
{
auto o = new Object;
return o; // should be ok (, but cannot in current conservative rule)
}

class C1 { C2 c2; }
class C2 { C3 c3; }
class C3 { C1 c1; }
immutable(C1) recursive1(C3 pc) pure { auto c = new C1(); return c; } // should be error, because pc.c1 == C1
immutable(C1) recursive2(C2 pc) pure { auto c = new C1(); return c; } // should be error, because pc.c3.c1 == C1
immutable(C3) recursive3(C1 pc) pure { auto c = new C3(); return c; } // should be error, c.c1 may be pc
immutable(C3) recursive4(C2 pc) pure { auto c = new C3(); return c; } // should be error, c.c1.c2 may be pc
immutable(C1) recursive5(shared C2 pc) pure { auto c = new C1(); return c; }
immutable(C1) recursive6(immutable C2 pc) pure { auto c = new C1(); return c; }
immutable(C1) recursiveE(immutable C2 pc) pure { auto c = new X1(); return c; }

class C4 { C4[] arr; }
immutable(C4)[] recursive7(int[] arr) pure { auto a = new C4[](1); return a; }

struct S1 { int* ptr; }
immutable(S1) foo1a( int*[] prm) pure { S1 s; return s; } // NG
immutable(S1) foo1b( const int*[] prm) pure { S1 s; return s; } // OK
immutable(S1) foo1c(immutable int*[] prm) pure { S1 s; return s; } // OK
immutable(int*[]) bar1a( S1 prm) pure { int *[] a; return a; } // NG
immutable(int*[]) bar1b( S1 prm) pure { const(int)*[] a; return a; } // NG
immutable(int*[]) bar1c( S1 prm) pure { immutable(int)*[] a; return a; } // OK

struct S2 { const(int)* ptr; }
immutable(S2) foo2a( int*[] prm) pure { S2 s; return s; } // OK
immutable(S2) foo2b( const int*[] prm) pure { S2 s; return s; } // NG
immutable(S2) foo2c(immutable int*[] prm) pure { S2 s; return s; } // NG
immutable(int*[]) bar2a( S2 prm) pure { int *[] a; return a; } // OK
immutable(int*[]) bar2b( S2 prm) pure { const(int)*[] a; return a; } // NG
immutable(int*[]) bar2c( S2 prm) pure { immutable(int)*[] a; return a; } // OK
77 changes: 77 additions & 0 deletions test/runnable/testconst.d
Expand Up @@ -2743,6 +2743,82 @@ void test8212()
S8212 s2 = s8212;
}

/************************************/
// 8408

template hasMutableIndirection8408(T)
{
template Unqual(T)
{
static if (is(T U == shared(const U))) alias U Unqual;
else static if (is(T U == const U )) alias U Unqual;
else static if (is(T U == immutable U )) alias U Unqual;
else static if (is(T U == inout U )) alias U Unqual;
else static if (is(T U == shared U )) alias U Unqual;
else alias T Unqual;
}

enum hasMutableIndirection8408 = !is(typeof({ Unqual!T t = void; immutable T u = t; }));
}
static assert(!hasMutableIndirection8408!(int));
static assert(!hasMutableIndirection8408!(int[3]));
static assert( hasMutableIndirection8408!(Object));

auto dup8408(E)(inout(E)[] arr) pure @trusted
{
static if (hasMutableIndirection8408!E)
{
auto copy = new E[](arr.length);
copy[] = cast(E[])arr[]; // assume constant
return cast(inout(E)[])copy; // assume constant
}
else
{
auto copy = new E[](arr.length);
copy[] = arr[];
return copy;
}
}

void test8408()
{
void test(E, bool constConv)()
{
E[] marr = [E.init, E.init, E.init];
immutable E[] iarr = [E.init, E.init, E.init];

E[] m2m = marr.dup8408(); assert(m2m == marr);
immutable E[] i2i = iarr.dup8408(); assert(i2i == iarr);

static if (constConv)
{ // If dup() hss strong purity, implicit conversion is allowed
immutable E[] m2i = marr.dup8408(); assert(m2i == marr);
E[] i2m = iarr.dup8408(); assert(i2m == iarr);
}
else
{
static assert(!is(typeof({ immutable E[] m2i = marr.dup8408(); })));
static assert(!is(typeof({ E[] i2m = iarr.dup8408(); })));
}
}

class C {}
struct S1 { long n; }
struct S2 { int* p; }
struct T1 { S1 s; }
struct T2 { S2 s; }
struct T3 { S1 s1; S2 s2; }

test!(int , true )();
test!(int[3], true )();
test!(C , false)();
test!(S1 , true )();
test!(S2 , false)();
test!(T1 , true )();
test!(T2 , false)();
test!(T3 , false)();
}

/************************************/
// 8688

Expand Down Expand Up @@ -2925,6 +3001,7 @@ int main()
test8099();
test8201();
test8212();
test8408();
test8688();
test9046();
test9090();
Expand Down

0 comments on commit f3b5817

Please sign in to comment.