Skip to content

Commit

Permalink
Merge pull request #2455 from 9rnsr/fix_inout
Browse files Browse the repository at this point in the history
Improve inout substitution algorithm for function pointer and delegate types
  • Loading branch information
WalterBright committed Aug 18, 2013
2 parents 558fffa + 988eae6 commit 1a6da16
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 6 deletions.
36 changes: 34 additions & 2 deletions src/expression.c
Expand Up @@ -1575,7 +1575,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
break;
}
if (wildmatch)
{ /* Calculate wild matching modifier
{
/* Calculate wild matching modifier
*/
if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
wildmatch = MODconst;
Expand All @@ -1584,9 +1585,40 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
else if (wildmatch & MODwild)
wildmatch = MODwild;
else
{ assert(wildmatch & MODmutable);
{
assert(wildmatch & MODmutable);
wildmatch = MODmutable;
}

if ((wildmatch == MODmutable || wildmatch == MODimmutable) &&
tf->next->hasWild() &&
(tf->isref || !tf->next->implicitConvTo(tf->next->immutableOf())))
{
if (fd && fd->isNested())
{
FuncDeclaration *f = fd->toParent2()->isFuncDeclaration();
for (; f; f = f->toParent2()->isFuncDeclaration())
{
/* If the called nested function may return the reference to
* outer inout data, it should be rejected.
*
* void foo(ref inout(int) x) {
* ref inout(int) bar(inout(int)) { return x; }
* bar(int.init) = 1; // bad!
* }
*/
if (((TypeFunction *)f->type)->iswild)
goto Linouterr;
}
}
else if (!fd && tf->isWild())
{
Linouterr:
const char *s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch);
error(loc, "modify inout to %s is not allowed inside inout function", s);
return Type::terror;
}
}
}

assert(nargs >= nparams);
Expand Down
60 changes: 57 additions & 3 deletions src/mtype.c
Expand Up @@ -1910,10 +1910,17 @@ Type *Type::substWildTo(unsigned mod)
//printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod);
Type *t;

if (nextOf())
if (Type *tn = nextOf())
{
t = nextOf()->substWildTo(mod);
if (t == nextOf())
// substitution has no effect on function pointer type.
if (ty == Tpointer && tn->ty == Tfunction)
{
t = this;
goto L1;
}

t = tn->substWildTo(mod);
if (t == tn)
t = this;
else
{
Expand All @@ -1928,6 +1935,10 @@ Type *Type::substWildTo(unsigned mod)
t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy());
((TypeAArray *)t)->sc = ((TypeAArray *)this)->sc; // duplicate scope
}
else if (ty == Tdelegate)
{
t = new TypeDelegate(t);
}
else
assert(0);

Expand All @@ -1937,6 +1948,7 @@ Type *Type::substWildTo(unsigned mod)
else
t = this;

L1:
if (isWild())
{
if (mod & MODconst)
Expand All @@ -1953,6 +1965,47 @@ Type *Type::substWildTo(unsigned mod)
return t;
}

Type *TypeFunction::substWildTo(unsigned)
{
if (!iswild && !(mod & MODwild))
return this;

// Substitude inout qualifier of function type to mutable or immutable
// would break type system. Instead substitude inout to the most weak
// qualifer - const.
unsigned m = MODconst;

assert(next);
Type *tret = next->substWildTo(m);
Parameters *params = parameters;
if (mod & MODwild)
params = parameters->copy();
for (size_t i = 0; i < params->dim; i++)
{
Parameter *p = (*params)[i];
Type *t = p->type->substWildTo(m);
if (t == p->type)
continue;
if (params == parameters)
params = parameters->copy();
(*params)[i] = new Parameter(p->storageClass, t, NULL, NULL);
}
if (next == tret && params == parameters)
return this;

// Similar to TypeFunction::syntaxCopy;
TypeFunction *t = new TypeFunction(params, tret, varargs, linkage);
t->mod = ((mod & MODwild) ? (mod & ~MODwild) | MODconst : mod);
t->isnothrow = isnothrow;
t->purity = purity;
t->isproperty = isproperty;
t->isref = isref;
t->iswild = false; // done
t->trust = trust;
t->fargs = fargs;
return t->merge();
}

/**************************
* Return type with the top level of it being mutable.
*/
Expand Down Expand Up @@ -5107,6 +5160,7 @@ Type *TypeFunction::syntaxCopy()
t->purity = purity;
t->isproperty = isproperty;
t->isref = isref;
t->iswild = iswild;
t->trust = trust;
t->fargs = fargs;
return t;
Expand Down
3 changes: 2 additions & 1 deletion src/mtype.h
Expand Up @@ -307,7 +307,7 @@ class Type : public RootObject
virtual MATCH implicitConvTo(Type *to);
virtual MATCH constConv(Type *to);
virtual unsigned wildConvTo(Type *tprm);
Type *substWildTo(unsigned mod);
virtual Type *substWildTo(unsigned mod);
virtual Type *toHeadMutable();
virtual ClassDeclaration *isClassHandle();
virtual Expression *getProperty(Loc loc, Identifier *ident, int flag);
Expand Down Expand Up @@ -691,6 +691,7 @@ class TypeFunction : public TypeNext
bool parameterEscapes(Parameter *p);
Type *addStorageClass(StorageClass stc);

Type *substWildTo(unsigned mod);
MATCH callMatch(Type *tthis, Expressions *toargs, int flag = 0);
type *toCtype();
RET retStyle();
Expand Down
186 changes: 186 additions & 0 deletions test/runnable/testconst.d
Expand Up @@ -2991,6 +2991,192 @@ void test9209() {
bar9209(f);
}

/************************************/
// 10758

struct X10758
{
static:
inout(int) screwUpVal(ref inout(int) wx) { return wx; }
ref inout(int) screwUpRef(ref inout(int) wx) { return wx; }
inout(int)* screwUpPtr(ref inout(int) wx) { return &wx; }
inout(int)[] screwUpArr(ref inout(int) wx) { return (&wx)[0 .. 1]; }
}

struct S10758
{
int x;
inout(int) screwUpVal(ref inout(int) _) inout { return x; }
ref inout(int) screwUpRef(ref inout(int) _) inout { return x; }
inout(int)* screwUpPtr(ref inout(int) _) inout { return &x; }
inout(int)[] screwUpArr(ref inout(int) _) inout { return (&x)[0 .. 1]; }
}

void test10758(ref inout(int) wx, inout(int)* wp, inout(int)[] wa, inout(S10758) ws)
{
inout(int) screwUpVal(inout(int) _) { return wx; }
ref inout(int) screwUpRef(inout(int) _) { return wx; }
inout(int)* screwUpPtr(inout(int) _) { return &wx; }
inout(int)[] screwUpArr(inout(int) _) { return (&wx)[0 .. 1]; }

int mx = 1;
const(int) cx = 1;
immutable(int) ix = 1;

// nested inout function may return an inout reference of the context,
// so substitude inout to mutable or immutable should be disallowed.
{
// value return does not leak any inout reference, so safe.
screwUpVal(mx);
screwUpVal(ix);
screwUpVal(wx);
screwUpVal(cx);

static assert(!__traits(compiles, screwUpRef(mx)));
static assert(!__traits(compiles, screwUpRef(ix)));
screwUpRef(wx);
screwUpRef(cx);

static assert(!__traits(compiles, screwUpPtr(mx)));
static assert(!__traits(compiles, screwUpPtr(ix)));
screwUpPtr(wx);
screwUpPtr(cx);

static assert(!__traits(compiles, screwUpArr(mx)));
static assert(!__traits(compiles, screwUpArr(ix)));
screwUpArr(cx);
screwUpArr(wx);
}

// function pointer holds no context, so there's no screw up.
{
auto fp_screwUpVal = &X10758.screwUpVal;
fp_screwUpVal(mx);
fp_screwUpVal(ix);
fp_screwUpVal(wx);
fp_screwUpVal(cx);

auto fp_screwUpRef = &X10758.screwUpRef;
fp_screwUpRef(mx);
fp_screwUpRef(ix);
fp_screwUpRef(wx);
fp_screwUpRef(cx);

auto fp_screwUpPtr = &X10758.screwUpVal;
fp_screwUpPtr(mx);
fp_screwUpPtr(ix);
fp_screwUpPtr(wx);
fp_screwUpPtr(cx);

auto fp_screwUpArr = &X10758.screwUpVal;
fp_screwUpArr(mx);
fp_screwUpArr(ix);
fp_screwUpArr(wx);
fp_screwUpArr(cx);
}

// inout delegate behaves same as nested functions.
{
auto dg_screwUpVal = &ws.screwUpVal;
dg_screwUpVal(mx);
dg_screwUpVal(ix);
dg_screwUpVal(wx);
dg_screwUpVal(cx);

auto dg_screwUpRef = &ws.screwUpRef;
static assert(!__traits(compiles, dg_screwUpRef(mx)));
static assert(!__traits(compiles, dg_screwUpRef(ix)));
dg_screwUpRef(wx);
dg_screwUpRef(cx);

auto dg_screwUpPtr = &ws.screwUpPtr;
static assert(!__traits(compiles, dg_screwUpPtr(mx)));
static assert(!__traits(compiles, dg_screwUpPtr(ix)));
dg_screwUpPtr(wx);
dg_screwUpPtr(cx);

auto dg_screwUpArr = &ws.screwUpArr;
static assert(!__traits(compiles, dg_screwUpArr(mx)));
static assert(!__traits(compiles, dg_screwUpArr(ix)));
dg_screwUpArr(cx);
dg_screwUpArr(wx);
}
}

/************************************/
// 10761

inout(int)* function(inout(int)*) fptr10761(inout(int)*)
{
static inout(int)* screwUp(inout(int)* x) { return x; }
auto fp = &screwUp;
static assert(is(typeof(fp) == inout(int)* function(inout(int)*)));
return fp;
}

inout(int)* delegate(inout(int)*) nest10761(inout(int)* x)
{
inout(int)* screwUp(inout(int)* _) { return x; }
auto dg = &screwUp;
static assert(is(typeof(dg) == inout(int)* delegate(inout(int)*)));
return dg;
}

struct S10761
{
int x;
inout(int)* screwUp() inout { return &x; }
}

inout(int)* delegate() inout memfn10761(inout(int)* x)
{
auto s = new inout S10761(1);
auto dg = &s.screwUp;
static assert(is(typeof(dg) == inout(int)* delegate() inout));
return dg;
}

void test10761()
{
int mx = 1;
const(int) cx = 1;
immutable(int) ix = 1;

// inout substitution has no effect on function pointer type
{
auto fp_m = fptr10761(&mx);
auto fp_c = fptr10761(&cx);
auto fp_i = fptr10761(&ix);
alias FP = inout(int)* function(inout(int)*);
static assert(is(typeof(fp_m) == FP));
static assert(is(typeof(fp_c) == FP));
static assert(is(typeof(fp_i) == FP));
}

// inout substitution on delegate type should always
// modify inout to const.
{
auto dg_m = nest10761(&mx);
auto dg_c = nest10761(&cx);
auto dg_i = nest10761(&ix);
alias DG = const(int)* delegate(const(int)*);
static assert(is(typeof(dg_m) == DG));
static assert(is(typeof(dg_c) == DG));
static assert(is(typeof(dg_i) == DG));
}

// same as above
{
auto dg_m = memfn10761(&mx);
auto dg_c = memfn10761(&cx);
auto dg_i = memfn10761(&ix);
alias DG = const(int)* delegate() const;
static assert(is(typeof(dg_m) == DG));
static assert(is(typeof(dg_c) == DG));
static assert(is(typeof(dg_i) == DG));
}
}

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

int main()
Expand Down

0 comments on commit 1a6da16

Please sign in to comment.