Skip to content

Commit d612c94

Browse files
committed
add inference of 'scope' for parameters for inferrable functions
1 parent 128f3fa commit d612c94

File tree

8 files changed

+88
-51
lines changed

8 files changed

+88
-51
lines changed

src/declaration.d

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ enum STCout = (1L << 12); // out parameter
9595
enum STClazy = (1L << 13); // lazy parameter
9696
enum STCforeach = (1L << 14); // variable for foreach loop
9797
// (1L << 15)
98-
enum STCvariadic = (1L << 16); // variadic function argument
98+
enum STCvariadic = (1L << 16); // the 'variadic' parameter in: T foo(T a, U b, V variadic...)
9999
enum STCctorinit = (1L << 17); // can only be set inside constructor
100100
enum STCtemplateparameter = (1L << 18); // template parameter
101101
enum STCscope = (1L << 19);
@@ -127,6 +127,7 @@ enum STCreturn = (1L << 44); // 'return ref' for function paramet
127127
enum STCautoref = (1L << 45); // Mark for the already deduced 'auto ref' parameter
128128
enum STCinference = (1L << 46); // do attribute inference
129129
enum STCexptemp = (1L << 47); // temporary variable that has lifetime restricted to an expression
130+
enum STCmaybescope = (1L << 48); // parameter might be 'scope'
130131

131132
enum STC_TYPECTOR = (STCconst | STCimmutable | STCshared | STCwild);
132133
enum STC_FUNCATTR = (STCref | STCnothrow | STCnogc | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem);

src/dtemplate.d

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
16251625
// https://issues.dlang.org/show_bug.cgi?id=12876
16261626
// Optimize argument to allow CT-known length matching
16271627
farg = farg.optimize(WANTvalue, (fparam.storageClass & (STCref | STCout)) != 0);
1628-
//printf("farg = %s %s\n", farg.type.toChars(), fargtoChars());
1628+
//printf("farg = %s %s\n", farg.type.toChars(), farg.toChars());
16291629

16301630
RootObject oarg = farg;
16311631
if ((fparam.storageClass & STCref) && (!(fparam.storageClass & STCauto) || farg.isLvalue()))
@@ -2501,6 +2501,7 @@ void functionResolve(Match* m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiar
25012501

25022502
int applyTemplate(TemplateDeclaration td)
25032503
{
2504+
//printf("applyTemplate()\n");
25042505
// skip duplicates
25052506
if (td == td_best)
25062507
return 0;
@@ -3675,7 +3676,15 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
36753676
{
36763677
Parameter a = Parameter.getNth(t.parameters, i);
36773678
Parameter ap = Parameter.getNth(tp.parameters, i);
3678-
if (a.storageClass != ap.storageClass || !deduceType(a.type, sc, ap.type, parameters, dedtypes))
3679+
3680+
enum stc = STCref | STCin | STCout | STClazy;
3681+
if ((a.storageClass & stc) != (ap.storageClass & stc) ||
3682+
// We can add scope, but not subtract it
3683+
(!(a.storageClass & STCscope) && (ap.storageClass & STCscope)) ||
3684+
// We can subtract return, but not add it
3685+
((a.storageClass & STCreturn) && !(ap.storageClass & STCreturn)) ||
3686+
3687+
!deduceType(a.type, sc, ap.type, parameters, dedtypes))
36793688
{
36803689
result = MATCHnomatch;
36813690
return;

src/escape.d

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import ddmd.visitor;
2727
import ddmd.arraytypes;
2828

2929
/****************************************
30-
* Function parameter param is being initialized to arg,
31-
* and params may escape.
30+
* Function parameter par is being initialized to arg,
31+
* and par may escape.
3232
* Detect if scoped values can escape this way.
3333
* Print error messages when these are detected.
3434
* Params:
@@ -39,9 +39,9 @@ import ddmd.arraytypes;
3939
* Returns:
4040
* true if pointers to the stack can escape via assignment
4141
*/
42-
bool checkParamArgumentEscape(Scope* sc, Parameter par, Expression arg, bool gag)
42+
bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Expression arg, bool gag)
4343
{
44-
//printf("checkParamArgumentEscape(arg: %s)\n", arg.toChars());
44+
//printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg.toChars(), par.toChars());
4545
//printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
4646

4747
if (!arg.type.hasPointers())
@@ -55,33 +55,39 @@ bool checkParamArgumentEscape(Scope* sc, Parameter par, Expression arg, bool gag
5555
return false;
5656

5757
bool result = false;
58+
59+
/* 'v' is assigned unsafely to 'par'
60+
*/
61+
void unsafeAssign(VarDeclaration v, const char* desc)
62+
{
63+
if (sc.func.setUnsafe())
64+
{
65+
if (!gag)
66+
error(arg.loc, "%s %s assigned to non-scope parameter %s calling %s",
67+
desc, v.toChars(), par.toChars(), fdc ? fdc.toPrettyChars() : "indirectly");
68+
result = true;
69+
}
70+
}
71+
5872
foreach (VarDeclaration v; er.byvalue)
5973
{
6074
if (v.isDataseg())
6175
continue;
6276

6377
Dsymbol p = v.toParent2();
6478

79+
v.storage_class &= ~STCmaybescope;
80+
6581
if (v.isScope())
6682
{
67-
if (sc.func.setUnsafe())
68-
{
69-
if (!gag)
70-
error(arg.loc, "scope variable %s assigned to non-scope parameter %s", v.toChars(), par.toChars());
71-
result = true;
72-
}
83+
unsafeAssign(v, "scope variable");
7384
}
7485
else if (v.storage_class & STCvariadic && p == sc.func)
7586
{
7687
Type tb = v.type.toBasetype();
7788
if (tb.ty == Tarray || tb.ty == Tsarray)
7889
{
79-
if (sc.func.setUnsafe())
80-
{
81-
if (!gag)
82-
error(arg.loc, "variadic variable %s assigned to non-scope parameter %s", v.toChars(), par.toChars());
83-
result = true;
84-
}
90+
unsafeAssign(v, "variadic variable");
8591
}
8692
}
8793
}
@@ -93,14 +99,11 @@ bool checkParamArgumentEscape(Scope* sc, Parameter par, Expression arg, bool gag
9399

94100
Dsymbol p = v.toParent2();
95101

102+
v.storage_class &= ~STCmaybescope;
103+
96104
if ((v.storage_class & (STCref | STCout)) == 0 && p == sc.func)
97105
{
98-
if (sc.func.setUnsafe())
99-
{
100-
if (!gag)
101-
error(arg.loc, "reference to local variable %s assigned to non-scope parameter %s", v.toChars(), par.toChars());
102-
result = true;
103-
}
106+
unsafeAssign(v, "reference to local variable");
104107
continue;
105108
}
106109
}
@@ -119,14 +122,11 @@ bool checkParamArgumentEscape(Scope* sc, Parameter par, Expression arg, bool gag
119122

120123
Dsymbol p = v.toParent2();
121124

125+
v.storage_class &= ~STCmaybescope;
126+
122127
if ((v.storage_class & (STCref | STCout | STCscope)) && p == sc.func)
123128
{
124-
if (sc.func.setUnsafe())
125-
{
126-
if (!gag)
127-
error(arg.loc, "reference to local %s assigned to non-scope parameter %s in @safe code", v.toChars(), par.toChars());
128-
result = true;
129-
}
129+
unsafeAssign(v, "reference to local");
130130
continue;
131131
}
132132
}
@@ -202,6 +202,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
202202

203203
Dsymbol p = v.toParent2();
204204

205+
if (!(va && va.isScope()))
206+
v.storage_class &= ~STCmaybescope;
207+
205208
if (v.isScope())
206209
{
207210
if (va && !va.isDataseg())
@@ -245,6 +248,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
245248

246249
Dsymbol p = v.toParent2();
247250

251+
if (!(va && va.isScope()))
252+
v.storage_class &= ~STCmaybescope;
253+
248254
if ((v.storage_class & (STCref | STCout)) == 0 && p == sc.func)
249255
{
250256
if (va && !va.isDataseg())
@@ -278,6 +284,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
278284

279285
Dsymbol p = v.toParent2();
280286

287+
if (!(va && va.isScope()))
288+
v.storage_class &= ~STCmaybescope;
289+
281290
if ((v.storage_class & (STCref | STCout | STCscope)) && p == sc.func)
282291
{
283292
if (va && !va.isDataseg())

src/expression.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1697,7 +1697,7 @@ extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type t
16971697
* Check arg to see if it matters.
16981698
*/
16991699
if (global.params.safe)
1700-
err |= checkParamArgumentEscape(sc, p, arg, false);
1700+
err |= checkParamArgumentEscape(sc, fd, p, arg, false);
17011701
}
17021702
else
17031703
{

src/func.d

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ enum FUNCFLAGnothrowInprocess = 4; /// working on determining nothrow
396396
enum FUNCFLAGnogcInprocess = 8; /// working on determining @nogc
397397
enum FUNCFLAGreturnInprocess = 0x10; /// working on inferring 'return' for parameters
398398
enum FUNCFLAGinlineScanned = 0x20; /// function has been scanned for inline possibilities
399+
enum FUNCFLAGinferScope = 0x40; /// infer 'scope' for parameters
399400

400401

401402
/***********************************************************
@@ -1560,6 +1561,8 @@ extern (C++) class FuncDeclaration : Declaration
15601561
stc |= STCparameter;
15611562
if (f.varargs == 2 && i + 1 == nparams)
15621563
stc |= STCvariadic;
1564+
if (flags & FUNCFLAGinferScope)
1565+
stc |= STCmaybescope;
15631566
stc |= fparam.storageClass & (STCin | STCout | STCref | STCreturn | STCscope | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
15641567
v.storage_class = stc;
15651568
v.semantic(sc2);
@@ -2218,7 +2221,26 @@ extern (C++) class FuncDeclaration : Declaration
22182221
f.isnogc = true;
22192222
}
22202223

2221-
flags &= ~FUNCFLAGreturnInprocess;
2224+
flags &= ~(FUNCFLAGreturnInprocess | FUNCFLAGinferScope);
2225+
2226+
// Infer STCscope
2227+
if (parameters)
2228+
{
2229+
size_t nfparams = Parameter.dim(f.parameters);
2230+
assert(nfparams == parameters.dim);
2231+
foreach (u, v; *parameters)
2232+
{
2233+
if (v.storage_class & STCmaybescope)
2234+
{
2235+
//printf("Inferring scope for %s\n", v.toChars());
2236+
Parameter p = Parameter.getNth(f.parameters, u);
2237+
v.storage_class &= ~STCmaybescope;
2238+
v.storage_class |= STCscope;
2239+
p.storageClass |= STCscope;
2240+
assert(!(p.storageClass & STCmaybescope));
2241+
}
2242+
}
2243+
}
22222244

22232245
// reset deco to apply inference result to mangled name
22242246
if (f != type)
@@ -3095,6 +3117,10 @@ extern (C++) class FuncDeclaration : Declaration
30953117

30963118
if (!isVirtual() || introducing)
30973119
flags |= FUNCFLAGreturnInprocess;
3120+
3121+
// Initialize for inferring STCscope
3122+
if (global.params.safe)
3123+
flags |= FUNCFLAGinferScope;
30983124
}
30993125

31003126
final PURE isPure()
@@ -3542,14 +3568,14 @@ extern (C++) class FuncDeclaration : Declaration
35423568
for (size_t i = 0; i < closureVars.dim; i++)
35433569
{
35443570
VarDeclaration v = closureVars[i];
3545-
//printf("\tv = %s\n", v->toChars());
3571+
//printf("\tv = %s\n", v.toChars());
35463572

35473573
for (size_t j = 0; j < v.nestedrefs.dim; j++)
35483574
{
35493575
FuncDeclaration f = v.nestedrefs[j];
35503576
assert(f != this);
35513577

3552-
//printf("\t\tf = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf);
3578+
//printf("\t\tf = %p, %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f, f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf);
35533579

35543580
/* Look to see if f escapes. We consider all parents of f within
35553581
* this, and also all siblings which call f; if any of them escape,
@@ -3563,7 +3589,7 @@ extern (C++) class FuncDeclaration : Declaration
35633589
continue;
35643590
if (fx.isThis() || fx.tookAddressOf)
35653591
{
3566-
//printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx->toChars(), fx->isVirtual(), fx->isThis(), fx->tookAddressOf);
3592+
//printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx.toChars(), fx.isVirtual(), fx.isThis(), fx.tookAddressOf);
35673593

35683594
/* Mark as needing closure any functions between this and f
35693595
*/
@@ -3596,17 +3622,17 @@ extern (C++) class FuncDeclaration : Declaration
35963622
Type tret = (cast(TypeFunction)type).next;
35973623
assert(tret);
35983624
tret = tret.toBasetype();
3599-
//printf("\t\treturning %s\n", tret->toChars());
3625+
//printf("\t\treturning %s\n", tret.toChars());
36003626
if (tret.ty == Tclass || tret.ty == Tstruct)
36013627
{
36023628
Dsymbol st = tret.toDsymbol(null);
3603-
//printf("\t\treturning class/struct %s\n", tret->toChars());
3629+
//printf("\t\treturning class/struct %s\n", tret.toChars());
36043630
for (Dsymbol s = st.parent; s; s = s.parent)
36053631
{
3606-
//printf("\t\t\tparent = %s %s\n", s->kind(), s->toChars());
3632+
//printf("\t\t\tparent = %s %s\n", s.kind(), s.toChars());
36073633
if (s == this)
36083634
{
3609-
//printf("\t\treturning local %s\n", st->toChars());
3635+
//printf("\t\treturning local %s\n", st.toChars());
36103636
goto Lyes;
36113637
}
36123638
}

src/parse.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2863,8 +2863,8 @@ final class Parser : Lexer
28632863
// if stc is not a power of 2
28642864
if (stc & (stc - 1) && !(stc == (STCin | STCref)))
28652865
error("incompatible parameter storage classes");
2866-
if ((storageClass & STCscope) && (storageClass & (STCref | STCout)))
2867-
error("scope cannot be ref or out");
2866+
//if ((storageClass & STCscope) && (storageClass & (STCref | STCout)))
2867+
//error("scope cannot be ref or out");
28682868

28692869
Token* t;
28702870
if (tpl && token.value == TOKidentifier && (t = peek(&token), (t.value == TOKcomma || t.value == TOKrparen || t.value == TOKdotdotdot)))

test/fail_compilation/fail191.d

Lines changed: 0 additions & 8 deletions
This file was deleted.

test/fail_compilation/retscope.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ struct HTTP
8686
/*
8787
TEST_OUTPUT:
8888
---
89-
fail_compilation/retscope.d(97): Error: reference to local variable sa assigned to non-scope parameter a
89+
fail_compilation/retscope.d(97): Error: reference to local variable sa assigned to non-scope parameter a calling retscope.bar8
9090
---
9191
*/
9292
// https://issues.dlang.org/show_bug.cgi?id=8838

0 commit comments

Comments
 (0)