Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Issue 4251 - Hole in the const system: immutable(T)[] implicitly casts to ref const(T)[] #558

Merged
merged 5 commits into from

4 participants

@9rnsr
Collaborator

http://d.puremagic.com/issues/show_bug.cgi?id=4251
This pull request requires #425 doe to prevent druntime breaking.

(@yebblies had posted #115, but it was a bit complicated than I had thought.)

When implicitly converting types with indirections, we consider only tail part of the types.
e.g. The tail part of T*** is T**.
There are two rules:

  • If the tail of destination type is full const of the tail of source type, it is const convertible.
  • Otherwise the tails of source and destination should be same.

e.g.

T*** => const(T***) OK, dst tail is full const of src tail
T*** => const(T**)* OK, dst tail is full const of src tail
T*** => const(T*)** NG, dst tail is differnt from src tail
T*** => const(T)*** NG, dst tail is differnt from src tail
T*** => T*** OK, dst tail is same as src tail
immutable(T*)** => const(T*)** NG, dst tail is differnt from src tail

Additional fix:
Issue 5493 - Able to overwrite immutable data by passing through ref function parameter

@yebblies
Collaborator

This is great. My pull 115 was a terrible solution, aside from being wrong.
Does this fix bug 5493 as well?

@9rnsr
Collaborator

Yes. I'm editing above summary, and now commits are divided for 4251 and 5493.

@9rnsr
Collaborator

Added fix for 5493 + inout bug.
It is specified in digitalmars.D newsgroup by Steven Schveighoffer.

@braddr
Owner

Fails the dmd func lit test on all platforms. One example:
http://d.puremagic.com/test-results/pull.ghtml?runid=15013
../src/dmd -m32 -Irunnable -odtest_results/runnable -oftest_results/runnable/funclit_0 runnable/funclit.d
Segmentation fault

@9rnsr
Collaborator

This fail requires #593.

@9rnsr
Collaborator

Add test for "Issue 7202 - Hole in type system still present for delegates".

@WalterBright WalterBright merged commit 47ad405 into D-Programming-Language:master
@braddr braddr referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@braddr braddr referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@ghost Unknown referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@AlexeyProkhin AlexeyProkhin referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
83 src/expression.c
@@ -690,6 +690,19 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
unsigned n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
unsigned wildmatch = 0;
+ if (ethis && tf->isWild())
+ {
+ Type *t = ethis->type;
+ if (t->isWild())
+ wildmatch |= MODwild;
+ else if (t->isConst())
+ wildmatch |= MODconst;
+ else if (t->isImmutable())
+ wildmatch |= MODimmutable;
+ else
+ wildmatch |= MODmutable;
+ }
+
int done = 0;
for (size_t i = 0; i < n; i++)
{
@@ -699,7 +712,6 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
arg = arguments->tdata()[i];
else
arg = NULL;
- Type *tb;
if (i < nparams)
{
@@ -816,6 +828,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
arg = arg->semantic(sc);
//printf("\targ = '%s'\n", arg->toChars());
arguments->setDim(i + 1);
+ arguments->tdata()[i] = arg;
+ nargs = i + 1;
done = 1;
}
@@ -826,7 +840,42 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
if (mod)
{
wildmatch |= mod;
- arg = arg->implicitCastTo(sc, p->type->substWildTo(mod));
+ }
+ }
+ }
+ if (done)
+ break;
+ }
+ if (wildmatch)
+ { /* Calculate wild matching modifier
+ */
+ if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
+ wildmatch = MODconst;
+ else if (wildmatch & MODimmutable)
+ wildmatch = MODimmutable;
+ else if (wildmatch & MODwild)
+ wildmatch = MODwild;
+ else
+ { assert(wildmatch & MODmutable);
+ wildmatch = MODmutable;
+ }
+ }
+
+ assert(nargs >= nparams);
+ for (size_t i = 0; i < nargs; i++)
+ {
+ Expression *arg = arguments->tdata()[i];
+ assert(arg);
+
+ if (i < nparams)
+ {
+ Parameter *p = Parameter::getNth(tf->parameters, i);
+
+ if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
+ {
+ if (p->type->hasWild())
+ {
+ arg = arg->implicitCastTo(sc, p->type->substWildTo(wildmatch));
arg = arg->optimize(WANTvalue);
}
else if (p->type != arg->type)
@@ -851,7 +900,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
arg = arg->modifiableLvalue(sc, arg);
}
- tb = arg->type->toBasetype();
+ Type *tb = arg->type->toBasetype();
#if !SARRAYVALUE
// Convert static arrays to pointers
if (tb->ty == Tsarray)
@@ -946,7 +995,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
// Convert static arrays to dynamic arrays
// BUG: I don't think this is right for D2
- tb = arg->type->toBasetype();
+ Type *tb = arg->type->toBasetype();
if (tb->ty == Tsarray)
{ TypeSArray *ts = (TypeSArray *)tb;
Type *ta = ts->next->arrayOf();
@@ -977,21 +1026,6 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
arg = arg->optimize(WANTvalue);
L3:
arguments->tdata()[i] = arg;
- if (done)
- break;
- }
-
- if (ethis && tf->isWild())
- {
- Type *tthis = ethis->type;
- if (tthis->isWild())
- wildmatch |= MODwild;
- else if (tthis->isConst())
- wildmatch |= MODconst;
- else if (tthis->isImmutable())
- wildmatch |= MODimmutable;
- else
- wildmatch |= MODmutable;
}
// If D linkage and variadic, add _arguments[] as first argument
@@ -1008,16 +1042,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
{ /* Adjust function return type based on wildmatch
*/
//printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars());
- if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
- tret = tret->substWildTo(MODconst);
- else if (wildmatch & MODimmutable)
- tret = tret->substWildTo(MODimmutable);
- else if (wildmatch & MODwild)
- ;
- else
- { assert(wildmatch & MODmutable);
- tret = tret->substWildTo(MODmutable);
- }
+ tret = tret->substWildTo(wildmatch);
}
return tret;
}
View
167 src/mtype.c
@@ -2443,11 +2443,29 @@ Type *TypeNext::makeMutable()
}
MATCH TypeNext::constConv(Type *to)
-{ MATCH m = Type::constConv(to);
+{
+ //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to->toChars());
+ if (equals(to))
+ return MATCHexact;
+
+ if (!(ty == to->ty && MODimplicitConv(mod, to->mod)))
+ return MATCHnomatch;
- if (m == MATCHconst &&
- next->constConv(((TypeNext *)to)->next) == MATCHnomatch)
- m = MATCHnomatch;
+ Type *tn = to->nextOf();
+ if (!(tn && next->ty == tn->ty))
+ return MATCHnomatch;
+
+ MATCH m;
+ if (to->isConst()) // whole tail const conversion
+ { // Recursive shared level check
+ m = next->constConv(tn);
+ if (m == MATCHexact)
+ m = MATCHconst;
+ }
+ else
+ { //printf("\tnext => %s, to->next => %s\n", next->toChars(), tn->toChars());
+ m = next->equals(tn) ? MATCHconst : MATCHnomatch;
+ }
return m;
}
@@ -3723,21 +3741,27 @@ MATCH TypeSArray::implicitConvTo(Type *to)
return MATCHnomatch;
}
if (to->ty == Tarray)
- { int offset = 0;
+ {
TypeDArray *ta = (TypeDArray *)to;
if (!MODimplicitConv(next->mod, ta->next->mod))
return MATCHnomatch;
- if (next->equals(ta->next) ||
-// next->implicitConvTo(ta->next) >= MATCHconst ||
- next->constConv(ta->next) != MATCHnomatch ||
- (ta->next->isBaseOf(next, &offset) && offset == 0 &&
- !ta->next->isMutable()) ||
- ta->next->ty == Tvoid)
+ /* Allow conversion to void[]
+ */
+ if (ta->next->ty == Tvoid)
+ {
return MATCHconvert;
+ }
+
+ MATCH m = next->constConv(ta->next);
+ if (m != MATCHnomatch)
+ {
+ return MATCHconvert;
+ }
return MATCHnomatch;
}
+
if (to->ty == Tsarray)
{
if (this == to)
@@ -3966,7 +3990,7 @@ MATCH TypeDArray::implicitConvTo(Type *to)
}
if (to->ty == Tarray)
- { int offset = 0;
+ {
TypeDArray *ta = (TypeDArray *)to;
if (!MODimplicitConv(next->mod, ta->next->mod))
@@ -3993,23 +4017,6 @@ MATCH TypeDArray::implicitConvTo(Type *to)
m = MATCHconst;
return m;
}
-
-#if 0
- /* Allow conversions of T[][] to const(T)[][]
- */
- if (mod == ta->mod && next->ty == Tarray && ta->next->ty == Tarray)
- {
- m = next->implicitConvTo(ta->next);
- if (m == MATCHconst)
- return m;
- }
-#endif
-
- /* Conversion of array of derived to array of const(base)
- */
- if (ta->next->isBaseOf(next, &offset) && offset == 0 &&
- !ta->next->isMutable())
- return MATCHconvert;
}
return Type::implicitConvTo(to);
}
@@ -4419,8 +4426,7 @@ MATCH TypeAArray::constConv(Type *to)
// Pick the worst match
return mkey < mindex ? mkey : mindex;
}
- else
- return Type::constConv(to);
+ return Type::constConv(to);
}
/***************************** TypePointer *****************************/
@@ -4510,7 +4516,8 @@ MATCH TypePointer::implicitConvTo(Type *to)
return MATCHnomatch;
}
else if (to->ty == Tpointer)
- { TypePointer *tp = (TypePointer *)to;
+ {
+ TypePointer *tp = (TypePointer *)to;
assert(tp->next);
if (!MODimplicitConv(next->mod, tp->next->mod))
@@ -4537,12 +4544,6 @@ MATCH TypePointer::implicitConvTo(Type *to)
m = MATCHconst;
return m;
}
-
- /* Conversion of ptr to derived to ptr to base
- */
- int offset = 0;
- if (tp->next->isBaseOf(next, &offset) && offset == 0)
- return MATCHconvert;
}
return MATCHnomatch;
}
@@ -5401,7 +5402,7 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
{
//printf("TypeFunction::callMatch() %s\n", toChars());
MATCH match = MATCHexact; // assume exact match
- bool wildmatch = FALSE;
+ unsigned wildmatch = 0;
if (ethis)
{ Type *t = ethis->type;
@@ -5419,6 +5420,17 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
else
return MATCHnomatch;
}
+ if (isWild())
+ {
+ if (t->isWild())
+ wildmatch |= MODwild;
+ else if (t->isConst())
+ wildmatch |= MODconst;
+ else if (t->isImmutable())
+ wildmatch |= MODimmutable;
+ else
+ wildmatch |= MODmutable;
+ }
}
size_t nparams = Parameter::dim(parameters);
@@ -5432,9 +5444,40 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
match = MATCHconvert; // match ... with a "conversion" match level
}
+ for (size_t u = 0; u < nargs; u++)
+ {
+ if (u >= nparams)
+ break;
+ Parameter *p = Parameter::getNth(parameters, u);
+ Expression *arg = args->tdata()[u];
+ assert(arg);
+
+ if (!(p->storageClass & STClazy && p->type->ty == Tvoid && arg->type->ty != Tvoid))
+ {
+ unsigned mod = arg->type->wildConvTo(p->type);
+ if (mod)
+ {
+ wildmatch |= mod;
+ }
+ }
+ }
+ if (wildmatch)
+ { /* Calculate wild matching modifier
+ */
+ if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
+ wildmatch = MODconst;
+ else if (wildmatch & MODimmutable)
+ wildmatch = MODimmutable;
+ else if (wildmatch & MODwild)
+ wildmatch = MODwild;
+ else
+ { assert(wildmatch & MODmutable);
+ wildmatch = MODmutable;
+ }
+ }
+
for (size_t u = 0; u < nparams; u++)
{ MATCH m;
- Expression *arg;
// BUG: what about out and ref?
@@ -5446,7 +5489,8 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
continue;
goto L1; // try typesafe variadics
}
- arg = args->tdata()[u];
+ {
+ Expression *arg = args->tdata()[u];
assert(arg);
if (arg->op == TOKfunction)
@@ -5459,6 +5503,9 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
//printf("arg: %s, type: %s\n", arg->toChars(), arg->type->toChars());
+ Type *targ = arg->type;
+ Type *tprm = wildmatch ? p->type->substWildTo(wildmatch) : p->type;
+
// Non-lvalues do not match ref or out parameters
if (p->storageClass & (STCref | STCout))
{ if (!arg->isLvalue())
@@ -5470,32 +5517,31 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
/* Don't allow static arrays to be passed to mutable references
* to static arrays if the argument cannot be modified.
*/
- Type *targb = arg->type->toBasetype();
- Type *tparb = p->type->toBasetype();
+ Type *targb = targ->toBasetype();
+ Type *tprmb = tprm->toBasetype();
//printf("%s\n", targb->toChars());
- //printf("%s\n", tparb->toChars());
- if (targb->nextOf() && tparb->ty == Tsarray &&
- !MODimplicitConv(targb->nextOf()->mod, tparb->nextOf()->mod))
+ //printf("%s\n", tprmb->toChars());
+ if (targb->nextOf() && tprmb->ty == Tsarray &&
+ !MODimplicitConv(targb->nextOf()->mod, tprmb->nextOf()->mod))
+ goto Nomatch;
+
+ // ref variable behaves like head-const reference
+ if (arg->op != TOKstring && !targb->constConv(tprmb))
goto Nomatch;
}
- if (p->storageClass & STClazy && p->type->ty == Tvoid &&
- arg->type->ty != Tvoid)
+ if (p->storageClass & STClazy && tprm->ty == Tvoid && targ->ty != Tvoid)
m = MATCHconvert;
else
{
- //printf("%s of type %s implicitConvTo %s\n", arg->toChars(), arg->type->toChars(), p->type->toChars());
+ //printf("%s of type %s implicitConvTo %s\n", arg->toChars(), targ->toChars(), tprm->toChars());
if (flag)
// for partial ordering, value is an irrelevant mockup, just look at the type
- m = arg->type->implicitConvTo(p->type);
+ m = targ->implicitConvTo(tprm);
else
- m = arg->implicitConvTo(p->type);
+ m = arg->implicitConvTo(tprm);
//printf("match %d\n", m);
- if (m == MATCHnomatch && arg->type->wildConvTo(p->type))
- {
- wildmatch = TRUE; // mod matched to wild
- m = MATCHconst;
- }
+ }
}
/* prefer matching the element type rather than the array
@@ -5524,7 +5570,7 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
{ TypeArray *ta = (TypeArray *)tb;
for (; u < nargs; u++)
{
- arg = args->tdata()[u];
+ Expression *arg = args->tdata()[u];
assert(arg);
#if 1
if (arg->op == TOKfunction)
@@ -8008,6 +8054,13 @@ MATCH TypeClass::constConv(Type *to)
if (ty == to->ty && sym == ((TypeClass *)to)->sym &&
MODimplicitConv(mod, to->mod))
return MATCHconst;
+
+ /* Conversion derived to const(base)
+ */
+ int offset = 0;
+ if (to->isBaseOf(this, &offset) && offset == 0 && !to->isMutable())
+ return MATCHconvert;
+
return MATCHnomatch;
}
View
1  src/optimize.c
@@ -633,7 +633,6 @@ Expression *CastExp::optimize(int result)
// We can convert 'head const' to mutable
if (to->constOf()->equals(e1->type->constOf()))
-// if (to->constConv(e1->type) >= MATCHconst)
{
e1->type = type;
if (X) printf(" returning5 %s\n", e1->toChars());
View
6 src/template.c
@@ -1245,7 +1245,9 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objec
/* Remove top const for dynamic array types and pointer types
*/
if ((argtype->ty == Tarray || argtype->ty == Tpointer) &&
- !argtype->isMutable())
+ !argtype->isMutable() &&
+ (!(fparam->storageClass & STCref) ||
+ (fparam->storageClass & STCauto) && !farg->isLvalue()))
{
argtype = argtype->mutableOf();
}
@@ -1804,7 +1806,7 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc,
ti = new TemplateInstance(loc, td_best, tdargs);
ti->semantic(sc, fargs);
fd_best = ti->toAlias()->isFuncDeclaration();
- if (!fd_best)
+ if (!fd_best || !((TypeFunction*)fd_best->type)->callMatch(ethis, fargs, flags))
goto Lerror;
return fd_best;
View
18 test/runnable/template9.d
@@ -626,6 +626,21 @@ void foo10(T)(T prm)
pragma(msg, T);
static assert(is(T == const(int)[]));
}
+void boo10(T)(ref T val) // ref paramter doesn't remove top const
+{
+ pragma(msg, T);
+ static assert(is(T == const(int[])));
+}
+void goo10(T)(auto ref T val) // auto ref with lvalue doesn't
+{
+ pragma(msg, T);
+ static assert(is(T == const(int[])));
+}
+void hoo10(T)(auto ref T val) // auto ref with rvalue does
+{
+ pragma(msg, T);
+ static assert(is(T == const(int)[]));
+}
void bar10(T)(T prm)
{
pragma(msg, T);
@@ -636,6 +651,9 @@ void test10()
const a = [1,2,3];
static assert(is(typeof(a) == const(int[])));
foo10(a);
+ boo10(a);
+ goo10(a);
+ hoo10(cast(const(int[]))[1,2,3]);
int n;
const p = &n;
View
266 test/runnable/testconst.d
@@ -1894,6 +1894,252 @@ void test88()
}
/************************************/
+// 4251
+
+void test4251a()
+{
+ alias int T;
+
+ static assert(!is( immutable(T)** : const(T)** )); // NG, tail difference
+ static assert( is( immutable(T)** : const(T**) )); // OK, tail to const
+
+ static assert( is( T *** : T *** )); // OK, tail is same
+ static assert(!is( T *** : const(T)*** ));
+ static assert(!is( T *** : const(T*)** ));
+ static assert( is( T *** : const(T**)* )); // OK, tail to const
+ static assert( is( T *** : const(T***) )); // OK, tail to const
+ static assert(!is( T *** : immutable(T)*** ));
+ static assert(!is( T *** : immutable(T*)** ));
+ static assert(!is( T *** : immutable(T**)* ));
+ static assert(!is( T *** : immutable(T***) ));
+
+ static assert(!is( const(T)*** : T *** ));
+ static assert( is( const(T)*** : const(T)*** )); // OK, tail is same
+ static assert(!is( const(T)*** : const(T*)** ));
+ static assert( is( const(T)*** : const(T**)* )); // OK, tail to const
+ static assert( is( const(T)*** : const(T***) )); // OK, tail to const
+ static assert(!is( const(T)*** : immutable(T)*** ));
+ static assert(!is( const(T)*** : immutable(T*)** ));
+ static assert(!is( const(T)*** : immutable(T**)* ));
+ static assert(!is( const(T)*** : immutable(T***) ));
+
+ static assert(!is( const(T*)** : T *** ));
+ static assert(!is( const(T*)** : const(T)*** ));
+ static assert( is( const(T*)** : const(T*)** )); // OK, tail is same
+ static assert( is( const(T*)** : const(T**)* )); // OK, tail to const
+ static assert( is( const(T*)** : const(T***) )); // OK, tail to const
+ static assert(!is( const(T*)** : immutable(T)*** ));
+ static assert(!is( const(T*)** : immutable(T*)** ));
+ static assert(!is( const(T*)** : immutable(T**)* ));
+ static assert(!is( const(T*)** : immutable(T***) ));
+
+ static assert(!is( const(T**)* : T *** ));
+ static assert(!is( const(T**)* : const(T)*** ));
+ static assert(!is( const(T**)* : const(T*)** ));
+ static assert( is( const(T**)* : const(T**)* )); // OK, tail is same
+ static assert( is( const(T**)* : const(T***) )); // OK, tail is same
+ static assert(!is( const(T**)* : immutable(T)*** ));
+ static assert(!is( const(T**)* : immutable(T*)** ));
+ static assert(!is( const(T**)* : immutable(T**)* ));
+ static assert(!is( const(T**)* : immutable(T***) ));
+
+ static assert(!is( const(T***) : T *** ));
+ static assert(!is( const(T***) : const(T)*** ));
+ static assert(!is( const(T***) : const(T*)** ));
+ static assert( is( const(T***) : const(T**)* )); // OK, tail is same
+ static assert( is( const(T***) : const(T***) )); // OK, tail is same
+ static assert(!is( const(T***) : T *** ));
+ static assert(!is( const(T***) : immutable(T)*** ));
+ static assert(!is( const(T***) : immutable(T*)** ));
+ static assert(!is( const(T***) : immutable(T**)* ));
+ static assert(!is( const(T***) : immutable(T***) ));
+
+ static assert(!is( immutable(T)*** : T *** ));
+ static assert(!is( immutable(T)*** : const(T)*** ));
+ static assert(!is( immutable(T)*** : const(T*)** ));
+ static assert( is( immutable(T)*** : const(T**)* )); // OK, tail to const
+ static assert( is( immutable(T)*** : const(T***) )); // OK, tail to const
+ static assert( is( immutable(T)*** : immutable(T)*** )); // OK, tail is same
+ static assert(!is( immutable(T)*** : immutable(T*)** ));
+ static assert(!is( immutable(T)*** : immutable(T**)* ));
+ static assert(!is( immutable(T)*** : immutable(T***) ));
+
+ static assert(!is( immutable(T*)** : T *** ));
+ static assert(!is( immutable(T*)** : const(T)*** ));
+ static assert(!is( immutable(T*)** : const(T*)** ));
+ static assert( is( immutable(T*)** : const(T**)* )); // OK, tail to const
+ static assert( is( immutable(T*)** : const(T***) )); // OK, tail to const
+ static assert(!is( immutable(T*)** : immutable(T)*** ));
+ static assert( is( immutable(T*)** : immutable(T*)** )); // OK, tail is same
+ static assert(!is( immutable(T*)** : immutable(T**)* ));
+ static assert(!is( immutable(T*)** : immutable(T***) ));
+
+ static assert(!is( immutable(T**)* : T *** ));
+ static assert(!is( immutable(T**)* : const(T)*** ));
+ static assert(!is( immutable(T**)* : const(T*)** ));
+ static assert( is( immutable(T**)* : const(T**)* )); // OK, tail to const
+ static assert( is( immutable(T**)* : const(T***) )); // OK, tail to const
+ static assert(!is( immutable(T**)* : immutable(T)*** ));
+ static assert(!is( immutable(T**)* : immutable(T*)** ));
+ static assert( is( immutable(T**)* : immutable(T**)* )); // OK, tail is same
+ static assert( is( immutable(T**)* : immutable(T***) )); // OK, tail is same
+
+ static assert(!is( immutable(T***) : T *** ));
+ static assert(!is( immutable(T***) : const(T)*** ));
+ static assert(!is( immutable(T***) : const(T*)** ));
+ static assert( is( immutable(T***) : const(T**)* )); // OK, tail to const
+ static assert( is( immutable(T***) : const(T***) )); // OK, tail to const
+ static assert(!is( immutable(T***) : immutable(T)*** ));
+ static assert(!is( immutable(T***) : immutable(T*)** ));
+ static assert( is( immutable(T***) : immutable(T**)* )); // OK, tail is same
+ static assert( is( immutable(T***) : immutable(T***) )); // OK, tail is same
+
+ static assert( is( immutable(int)** : const(immutable(int)*)* )); // OK, tail to const
+
+ // shared level should be same
+ static assert(!is( shared(T)*** : const(T***) )); // NG, tail to const but shared level is different
+ static assert( is( shared(T***) : shared(const(T***)) )); // OK, tail to const and shared level is same
+
+ // head qualifier difference is ignored
+ static assert(is( shared(int)* : shared(int*) ));
+ static assert(is( inout (int)* : inout (int*) ));
+
+ //
+ static assert(!is( T** : T*** ));
+ static assert(!is( T[]** : T*** ));
+}
+
+void test4251b()
+{
+ class C {}
+ class D : C {}
+
+ static assert(!is( C[]* : const(C)[]* ));
+ static assert( is( C[]* : const(C[])* ));
+
+ // derived class to const(base class) in tail
+ static assert( is( D[] : const(C)[] ));
+ static assert( is( D[]* : const(C[])* ));
+
+ static assert( is( D* : const(C)* ));
+ static assert( is( D** : const(C*)* ));
+
+ // derived class to const(base interface) in tail
+ interface I {}
+ class X : I {}
+ static assert(!is( X[] : const(I)[] ));
+
+ // interface to const(base interface) in tail
+ interface J {}
+ interface K : I, J {}
+ static assert( is( K[] : const(I)[] )); // OK, runtime offset is same
+ static assert(!is( K[] : const(J)[] )); // NG, runtime offset is different
+}
+
+/************************************/
+// 5493
+
+void test5493()
+{
+ // non template function
+ void pifun(immutable(char)[]* a) {}
+ void rifun(ref immutable(char)[] a) {}
+
+ void pcfun(const(char)[]* a) {}
+ void rcfun(ref const(char)[] a) {}
+
+ immutable char[] buf1 = "hello";
+ static assert(!__traits(compiles, pifun(buf1)));
+ static assert(!__traits(compiles, pcfun(buf1)));
+ static assert(!__traits(compiles, rifun(buf1)));
+ static assert(!__traits(compiles, rcfun(buf1)));
+
+ immutable char[5] buf2 = "hello";
+ static assert(!__traits(compiles, pifun(buf2)));
+ static assert(!__traits(compiles, pcfun(buf2)));
+ static assert(!__traits(compiles, rifun(buf2)));
+ static assert(!__traits(compiles, rcfun(buf2)));
+
+ const char[] buf3 = "hello";
+ static assert(!__traits(compiles, pcfun(buf3)));
+ static assert(!__traits(compiles, rcfun(buf3)));
+
+ const char[5] buf4 = "hello";
+ static assert(!__traits(compiles, pcfun(buf4)));
+ static assert(!__traits(compiles, rcfun(buf4)));
+
+ // template function
+ void pmesswith(T)(const(T)[]* ts, const(T) t)
+ {
+ *ts ~= t;
+ }
+ void rmesswith(T)(ref const(T)[] ts, const(T) t)
+ {
+ ts ~= t;
+ }
+ class C
+ {
+ int x;
+ this(int i) { x = i; }
+ }
+ C[] cs;
+ immutable C ci = new immutable(C)(6);
+ assert (ci.x == 6);
+ static assert(!__traits(compiles, pmesswith(&cs,ci)));
+ static assert(!__traits(compiles, rmesswith(cs,ci)));
+ //cs[$-1].x = 14;
+ //assert (ci.x == 14); //whoops.
+}
+
+/************************************/
+// 5493 + inout
+
+void test5493inout()
+{
+ int m;
+ const(int) c;
+ immutable(int) i;
+
+ inout(int) ptrfoo(inout(int)** a, inout(int)* b)
+ {
+ *a = b;
+ return 0; // dummy
+ }
+ inout(int) reffoo(ref inout(int)* a, inout(int)* b)
+ {
+ a = b;
+ return 0; // dummy
+ }
+
+ // wild matching: inout == mutable
+ int* pm;
+ ptrfoo(&pm, &m); assert(pm == &m);
+ static assert(!__traits(compiles, ptrfoo(&pm, &c)));
+ static assert(!__traits(compiles, ptrfoo(&pm, &i)));
+ reffoo( pm, &m); assert(pm == &m);
+ static assert(!__traits(compiles, reffoo( pm, &c)));
+ static assert(!__traits(compiles, reffoo( pm, &i)));
+
+ // wild matching: inout == const
+ const(int)* pc;
+ ptrfoo(&pc, &m); assert(pc == &m);
+ ptrfoo(&pc, &c); assert(pc == &c);
+ ptrfoo(&pc, &i); assert(pc == &i);
+ reffoo( pc, &m); assert(pc == &m);
+ reffoo( pc, &c); assert(pc == &c);
+ reffoo( pc, &i); assert(pc == &i);
+
+ // wild matching: inout == immutable
+ immutable(int)* pi;
+ static assert(!__traits(compiles, ptrfoo(&pi, &m)));
+ static assert(!__traits(compiles, ptrfoo(&pi, &c)));
+ ptrfoo(&pi, &i); assert(pi == &i);
+ static assert(!__traits(compiles, reffoo( pi, &m)));
+ static assert(!__traits(compiles, reffoo( pi, &c)));
+ reffoo( pi, &i); assert(pi == &i);
+}
+
+/************************************/
// 6782
struct Tuple6782(T...)
@@ -2208,6 +2454,21 @@ void test6940()
}
/************************************/
+// 7202
+
+void test7202()
+{
+ void writeln(string s) @system { printf("%.*s\n", s.length, s.ptr); }
+ void delegate() @system x = { writeln("I am @system"); };
+ void delegate() @safe y = { };
+ auto px = &x;
+ auto py = &y;
+ static assert(!__traits(compiles, px = py)); // accepts-invalid
+ *px = x;
+ y(); // "I am @system" -> no output, OK
+}
+
+/************************************/
int main()
{
@@ -2300,6 +2561,10 @@ int main()
test1961b();
test1961c();
test88();
+ test4251a();
+ test4251b();
+ test5493();
+ test5493inout();
test6782();
test6864();
test6865();
@@ -2308,6 +2573,7 @@ int main()
test6912();
test6939();
test6940();
+ test7202();
printf("Success\n");
return 0;
Something went wrong with that request. Please try again.