Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

return scope: first support #5972

Merged
merged 14 commits into from Nov 1, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/declaration.d
Expand Up @@ -95,7 +95,7 @@ enum STCout = (1L << 12); // out parameter
enum STClazy = (1L << 13); // lazy parameter
enum STCforeach = (1L << 14); // variable for foreach loop
// (1L << 15)
enum STCvariadic = (1L << 16); // variadic function argument
enum STCvariadic = (1L << 16); // the 'variadic' parameter in: T foo(T a, U b, V variadic...)
enum STCctorinit = (1L << 17); // can only be set inside constructor
enum STCtemplateparameter = (1L << 18); // template parameter
enum STCscope = (1L << 19);
Expand Down Expand Up @@ -127,6 +127,7 @@ enum STCreturn = (1L << 44); // 'return ref' for function paramet
enum STCautoref = (1L << 45); // Mark for the already deduced 'auto ref' parameter
enum STCinference = (1L << 46); // do attribute inference
enum STCexptemp = (1L << 47); // temporary variable that has lifetime restricted to an expression
enum STCmaybescope = (1L << 48); // parameter might be 'scope'

enum STC_TYPECTOR = (STCconst | STCimmutable | STCshared | STCwild);
enum STC_FUNCATTR = (STCref | STCnothrow | STCnogc | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem);
Expand Down
13 changes: 11 additions & 2 deletions src/dtemplate.d
Expand Up @@ -1625,7 +1625,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
// https://issues.dlang.org/show_bug.cgi?id=12876
// Optimize argument to allow CT-known length matching
farg = farg.optimize(WANTvalue, (fparam.storageClass & (STCref | STCout)) != 0);
//printf("farg = %s %s\n", farg.type.toChars(), fargtoChars());
//printf("farg = %s %s\n", farg.type.toChars(), farg.toChars());

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

int applyTemplate(TemplateDeclaration td)
{
//printf("applyTemplate()\n");
// skip duplicates
if (td == td_best)
return 0;
Expand Down Expand Up @@ -3675,7 +3676,15 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
{
Parameter a = Parameter.getNth(t.parameters, i);
Parameter ap = Parameter.getNth(tp.parameters, i);
if (a.storageClass != ap.storageClass || !deduceType(a.type, sc, ap.type, parameters, dedtypes))

enum stc = STCref | STCin | STCout | STClazy;
if ((a.storageClass & stc) != (ap.storageClass & stc) ||
// We can add scope, but not subtract it
(!(a.storageClass & STCscope) && (ap.storageClass & STCscope)) ||
// We can subtract return, but not add it
((a.storageClass & STCreturn) && !(ap.storageClass & STCreturn)) ||

!deduceType(a.type, sc, ap.type, parameters, dedtypes))
{
result = MATCHnomatch;
return;
Expand Down
65 changes: 37 additions & 28 deletions src/escape.d
Expand Up @@ -27,8 +27,8 @@ import ddmd.visitor;
import ddmd.arraytypes;

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

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

bool result = false;

/* 'v' is assigned unsafely to 'par'
*/
void unsafeAssign(VarDeclaration v, const char* desc)
{
if (sc.func.setUnsafe())
{
if (!gag)
error(arg.loc, "%s %s assigned to non-scope parameter %s calling %s",
desc, v.toChars(), par.toChars(), fdc ? fdc.toPrettyChars() : "indirectly");
result = true;
}
}

foreach (VarDeclaration v; er.byvalue)
{
if (v.isDataseg())
continue;

Dsymbol p = v.toParent2();

v.storage_class &= ~STCmaybescope;

if (v.isScope())
{
if (sc.func.setUnsafe())
{
if (!gag)
error(arg.loc, "scope variable %s assigned to non-scope parameter %s", v.toChars(), par.toChars());
result = true;
}
unsafeAssign(v, "scope variable");
}
else if (v.storage_class & STCvariadic && p == sc.func)
{
Type tb = v.type.toBasetype();
if (tb.ty == Tarray || tb.ty == Tsarray)
{
if (sc.func.setUnsafe())
{
if (!gag)
error(arg.loc, "variadic variable %s assigned to non-scope parameter %s", v.toChars(), par.toChars());
result = true;
}
unsafeAssign(v, "variadic variable");
}
}
}
Expand All @@ -93,14 +99,11 @@ bool checkParamArgumentEscape(Scope* sc, Parameter par, Expression arg, bool gag

Dsymbol p = v.toParent2();

v.storage_class &= ~STCmaybescope;

if ((v.storage_class & (STCref | STCout)) == 0 && p == sc.func)
{
if (sc.func.setUnsafe())
{
if (!gag)
error(arg.loc, "reference to local variable %s assigned to non-scope parameter %s", v.toChars(), par.toChars());
result = true;
}
unsafeAssign(v, "reference to local variable");
continue;
}
}
Expand All @@ -119,14 +122,11 @@ bool checkParamArgumentEscape(Scope* sc, Parameter par, Expression arg, bool gag

Dsymbol p = v.toParent2();

v.storage_class &= ~STCmaybescope;

if ((v.storage_class & (STCref | STCout | STCscope)) && p == sc.func)
{
if (sc.func.setUnsafe())
{
if (!gag)
error(arg.loc, "reference to local %s assigned to non-scope parameter %s in @safe code", v.toChars(), par.toChars());
result = true;
}
unsafeAssign(v, "reference to local");
continue;
}
}
Expand Down Expand Up @@ -202,6 +202,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)

Dsymbol p = v.toParent2();

if (!(va && va.isScope()))
v.storage_class &= ~STCmaybescope;

if (v.isScope())
{
if (va && !va.isDataseg())
Expand Down Expand Up @@ -245,6 +248,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)

Dsymbol p = v.toParent2();

if (!(va && va.isScope()))
v.storage_class &= ~STCmaybescope;

if ((v.storage_class & (STCref | STCout)) == 0 && p == sc.func)
{
if (va && !va.isDataseg())
Expand Down Expand Up @@ -278,6 +284,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)

Dsymbol p = v.toParent2();

if (!(va && va.isScope()))
v.storage_class &= ~STCmaybescope;

if ((v.storage_class & (STCref | STCout | STCscope)) && p == sc.func)
{
if (va && !va.isDataseg())
Expand Down
2 changes: 1 addition & 1 deletion src/expression.d
Expand Up @@ -1697,7 +1697,7 @@ extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type t
* Check arg to see if it matters.
*/
if (global.params.safe)
err |= checkParamArgumentEscape(sc, p, arg, false);
err |= checkParamArgumentEscape(sc, fd, p, arg, false);
}
else
{
Expand Down
42 changes: 34 additions & 8 deletions src/func.d
Expand Up @@ -396,6 +396,7 @@ enum FUNCFLAGnothrowInprocess = 4; /// working on determining nothrow
enum FUNCFLAGnogcInprocess = 8; /// working on determining @nogc
enum FUNCFLAGreturnInprocess = 0x10; /// working on inferring 'return' for parameters
enum FUNCFLAGinlineScanned = 0x20; /// function has been scanned for inline possibilities
enum FUNCFLAGinferScope = 0x40; /// infer 'scope' for parameters


/***********************************************************
Expand Down Expand Up @@ -1560,6 +1561,8 @@ extern (C++) class FuncDeclaration : Declaration
stc |= STCparameter;
if (f.varargs == 2 && i + 1 == nparams)
stc |= STCvariadic;
if (flags & FUNCFLAGinferScope)
stc |= STCmaybescope;
stc |= fparam.storageClass & (STCin | STCout | STCref | STCreturn | STCscope | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
v.storage_class = stc;
v.semantic(sc2);
Expand Down Expand Up @@ -2218,7 +2221,26 @@ extern (C++) class FuncDeclaration : Declaration
f.isnogc = true;
}

flags &= ~FUNCFLAGreturnInprocess;
flags &= ~(FUNCFLAGreturnInprocess | FUNCFLAGinferScope);

// Infer STCscope
if (parameters)
{
size_t nfparams = Parameter.dim(f.parameters);
assert(nfparams == parameters.dim);
foreach (u, v; *parameters)
{
if (v.storage_class & STCmaybescope)
{
//printf("Inferring scope for %s\n", v.toChars());
Parameter p = Parameter.getNth(f.parameters, u);
v.storage_class &= ~STCmaybescope;
v.storage_class |= STCscope;
p.storageClass |= STCscope;
assert(!(p.storageClass & STCmaybescope));
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this unset STCreturn if that doesn't apply?


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

if (!isVirtual() || introducing)
flags |= FUNCFLAGreturnInprocess;

// Initialize for inferring STCscope
if (global.params.safe)
flags |= FUNCFLAGinferScope;
}

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

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

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

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

/* Mark as needing closure any functions between this and f
*/
Expand Down Expand Up @@ -3596,17 +3622,17 @@ extern (C++) class FuncDeclaration : Declaration
Type tret = (cast(TypeFunction)type).next;
assert(tret);
tret = tret.toBasetype();
//printf("\t\treturning %s\n", tret->toChars());
//printf("\t\treturning %s\n", tret.toChars());
if (tret.ty == Tclass || tret.ty == Tstruct)
{
Dsymbol st = tret.toDsymbol(null);
//printf("\t\treturning class/struct %s\n", tret->toChars());
//printf("\t\treturning class/struct %s\n", tret.toChars());
for (Dsymbol s = st.parent; s; s = s.parent)
{
//printf("\t\t\tparent = %s %s\n", s->kind(), s->toChars());
//printf("\t\t\tparent = %s %s\n", s.kind(), s.toChars());
if (s == this)
{
//printf("\t\treturning local %s\n", st->toChars());
//printf("\t\treturning local %s\n", st.toChars());
goto Lyes;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/parse.d
Expand Up @@ -2863,8 +2863,8 @@ final class Parser : Lexer
// if stc is not a power of 2
if (stc & (stc - 1) && !(stc == (STCin | STCref)))
error("incompatible parameter storage classes");
if ((storageClass & STCscope) && (storageClass & (STCref | STCout)))
error("scope cannot be ref or out");
//if ((storageClass & STCscope) && (storageClass & (STCref | STCout)))
//error("scope cannot be ref or out");

Token* t;
if (tpl && token.value == TOKidentifier && (t = peek(&token), (t.value == TOKcomma || t.value == TOKrparen || t.value == TOKdotdotdot)))
Expand Down
8 changes: 0 additions & 8 deletions test/fail_compilation/fail191.d

This file was deleted.

2 changes: 1 addition & 1 deletion test/fail_compilation/retscope.d
Expand Up @@ -86,7 +86,7 @@ struct HTTP
/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(97): Error: reference to local variable sa assigned to non-scope parameter a
fail_compilation/retscope.d(97): Error: reference to local variable sa assigned to non-scope parameter a calling retscope.bar8
---
*/
// https://issues.dlang.org/show_bug.cgi?id=8838
Expand Down