Skip to content
Browse files

fix Issue 8408 - Purity calculation should be improved

  • Loading branch information...
1 parent bfd24e0 commit 41c52a324d0526a079039041c64afc1d3983eb58 @9rnsr 9rnsr committed
Showing with 150 additions and 42 deletions.
  1. +2 −0 src/func.c
  2. +71 −42 src/mtype.c
  3. +77 −0 test/runnable/testconst.d
View
2 src/func.c
@@ -2932,6 +2932,8 @@ enum PURE FuncDeclaration::isPureBypassingInference()
{
if (flags & FUNCFLAGpurityInprocess)
return PUREfwdref;
+ else if (type->nextOf() == NULL)
+ return PUREfwdref;
else
return isPure();
}
View
113 src/mtype.c
@@ -5712,77 +5712,106 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc)
}
+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;
+}
+
/********************************************
* Do this lazily, as the parameter types might be forward referenced.
*/
void TypeFunction::purityLevel()
{
+ //printf("purityLevel(%s)\n", toChars());
+
TypeFunction *tf = this;
- if (tf->purity == PUREfwdref)
+ if (tf->purity == PUREfwdref && tf->next)
{ /* Evaluate what kind of purity based on the modifiers for the parameters
*/
- tf->purity = PUREstrong; // assume strong until something weakens it
- if (tf->parameters)
+ enum PURE purity = PUREstrong; // assume strong until something weakens it
+ size_t dim = Parameter::dim(tf->parameters);
+
+ if (dim)
{
- size_t dim = Parameter::dim(tf->parameters);
+ Type *tret = tf->next;
+ assert(tret);
+ Type *treti = tf->isref ? tret->toBasetype() : getIndirection(tret);
+ if (treti && (treti->mod & MODimmutable))
+ treti = NULL; // indirection is immutable
+ //printf(" tret = %s, treti = %s\n", tret->toChars(), treti ? treti->toChars() : "NULL");
+
for (size_t i = 0; i < dim; i++)
{ Parameter *fparam = Parameter::getNth(tf->parameters, i);
if (fparam->storageClass & STClazy)
{
- tf->purity = PUREweak;
+ purity = PUREweak;
break;
}
if (fparam->storageClass & STCout)
{
- tf->purity = PUREweak;
+ purity = PUREweak;
break;
}
if (!fparam->type)
continue;
- if (fparam->storageClass & STCref)
- {
- if (!(fparam->type->mod & (MODconst | MODimmutable | MODwild)))
- { tf->purity = PUREweak;
- break;
- }
- if (fparam->type->mod & MODconst)
- { tf->purity = PUREconst;
- continue;
- }
+
+ Type *tprm = fparam->type;
+ Type *tprmi = fparam->storageClass & STCref ? tprm->toBasetype() : getIndirection(tprm);
+ //printf(" [%d] tprm = %s, tprmi = %s\n", i, tprm->toChars(), tprmi ? tprmi->toChars() : "NULL");
+
+ if (!tprmi || (tprmi->mod & MODimmutable))
+ continue; // there is no mutable indirection
+ if (tprmi->isMutable())
+ { purity = PUREweak; // indirection is mutable
+ break;
}
- Type *t = fparam->type->toBasetype();
- if (!t->hasPointers())
- continue;
- if (t->mod & (MODimmutable | MODwild))
+ if (!treti)
+ continue; // mutable indirection is never returned
+
+ if (purity < PUREstrong)
continue;
- /* The rest of this is too strict; fix later.
- * For example, the only pointer members of a struct may be immutable,
- * which would maintain strong purity.
- */
- if (t->mod & MODconst)
- { tf->purity = PUREconst;
+
+ // Determine the parameter is really PUREconst or not
+ assert(tprmi->mod & (MODconst | MODwild));
+ if (tprmi->constConv(treti)) // simple case
+ purity = PUREconst;
+ else if (tprmi->invariantOf()->equals(treti->invariantOf()))
continue;
+ else
+ {
+ /* The rest of this is little strict; fix later.
+ * For example:
+ *
+ * struct S { immutable* p; }
+ * pure S foo(const int* p);
+ *
+ * which would maintain strong purity.
+ */
+ if (tprmi->hasPointers() || treti->hasPointers())
+ purity = PUREconst;
}
- Type *tn = t->nextOf();
- if (tn)
- { tn = tn->toBasetype();
- if (tn->ty == Tpointer || tn->ty == Tarray)
- { /* Accept immutable(T)* and immutable(T)[] as being strongly pure
- */
- if (tn->mod & (MODimmutable | MODwild))
- continue;
- if (tn->mod & MODconst)
- { tf->purity = PUREconst;
- continue;
- }
- }
- }
+
/* Should catch delegates and function pointers, and fold in their purity
*/
- tf->purity = PUREweak; // err on the side of too strict
- break;
}
}
+
+ //printf(" --> purity: %d\n", purity);
+ tf->purity = purity;
}
}
View
77 test/runnable/testconst.d
@@ -2744,6 +2744,82 @@ void test8212()
}
/************************************/
+// 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
void test8688()
@@ -2875,6 +2951,7 @@ int main()
test8099();
test8201();
test8212();
+ test8408();
test8688();
printf("Success\n");

0 comments on commit 41c52a3

Please sign in to comment.
Something went wrong with that request. Please try again.