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

Make in a first-class storage class, not a const [scope] alias #11474

Merged
merged 2 commits into from Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions changelog/in-identity.dd
@@ -0,0 +1,11 @@
Parameters marked as `in` will now be properly displayed

In earlier releases, using `in` on parameters would be lowered too early,
leading to confusing display: error messages would show the parameter
as `const` (or `scope const` if `-preview=in` was used),
and so would header generations or stack traces.
The header generation was problematic, as a header generated with `-preview=in`
would be different from one generated without.
From this release, `in` will now display correctly in every message.
As this requires an update to the mangling, some older debuggers or tools
might not be able to properly demangle functions that uses `in` parameters.
2 changes: 1 addition & 1 deletion src/dmd/cli.d
Expand Up @@ -756,7 +756,7 @@ dmd -cov -unittest myprog.d
"enable rvalue arguments to ref parameters"),
Feature("nosharedaccess", "noSharedAccess",
"disable access to shared memory objects"),
Feature("in", "inMeansScopeConst",
Feature("in", "previewIn",
"in means scope const"),
];
}
Expand Down
8 changes: 7 additions & 1 deletion src/dmd/dmangle.d
Expand Up @@ -79,7 +79,7 @@ private immutable char[TMAX] mangleChar =
Tfunction : 'F', // D function
Tsarray : 'G',
Taarray : 'H',
Tident : 'I',
Copy link
Member

Choose a reason for hiding this comment

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

I am wondering why this change does not seem to break tests.
Where was Tident used? in templates?

Copy link
Member Author

Choose a reason for hiding this comment

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

// I // in
// J // out
// K // ref
// L // lazy
Expand All @@ -99,6 +99,7 @@ private immutable char[TMAX] mangleChar =
// Z // not variadic, end of parameters

// '@' shouldn't appear anywhere in the deco'd names
Tident : '@',
Tinstance : '@',
Terror : '@',
Ttypeof : '@',
Expand Down Expand Up @@ -1112,7 +1113,12 @@ public:
switch (p.storageClass & (STC.IOR | STC.lazy_))
{
case 0:
break;
case STC.in_:
buf.writeByte('I');
break;
case STC.in_ | STC.ref_:
buf.writestring("IK");
break;
case STC.out_:
buf.writeByte('J');
Expand Down
5 changes: 5 additions & 0 deletions src/dmd/dsymbolsem.d
Expand Up @@ -1102,6 +1102,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
dsym.storage_class &= ~stc; // strip off
}

// At this point we can add `scope` to the STC instead of `in`,
// because we are never going to use this variable's STC for user messages
if (dsym.storage_class & STC.in_ && global.params.previewIn)
dsym.storage_class |= STC.scope_;

if (dsym.storage_class & STC.scope_)
{
StorageClass stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.tls | STC.gshared);
Expand Down
2 changes: 1 addition & 1 deletion src/dmd/globals.d
Expand Up @@ -166,7 +166,7 @@ extern (C++) struct Param
bool useTypeInfo = true; // generate runtime type information
bool useExceptions = true; // support exception handling
bool noSharedAccess; // read/write access to shared memory objects
bool inMeansScopeConst; // `in` means `scope const`
bool previewIn; // `in` means `scope const`
bool betterC; // be a "better C" compiler; no dependency on D runtime
bool addMain; // add a default main() function
bool allInst; // generate code for all template instantiations
Expand Down
2 changes: 1 addition & 1 deletion src/dmd/globals.h
Expand Up @@ -144,7 +144,7 @@ struct Param
bool useTypeInfo; // generate runtime type information
bool useExceptions; // support exception handling
bool noSharedAccess; // read/write access to shared memory objects
bool inMeansScopeConst; // `in` means `scope const`
bool previewIn; // `in` means `scope const`
bool betterC; // be a "better C" compiler; no dependency on D runtime
bool addMain; // add a default main() function
bool allInst; // generate code for all template instantiations
Expand Down
18 changes: 10 additions & 8 deletions src/dmd/hdrgen.d
Expand Up @@ -3057,17 +3057,18 @@ private void parameterToBuffer(Parameter p, OutBuffer* buf, HdrGenState* hgs)
if (p.storageClass & STC.return_)
buf.writestring("return ");

if (p.storageClass & STC.out_)
buf.writestring("out ");
else if (p.storageClass & STC.ref_)
buf.writestring("ref ");
else if (p.storageClass & STC.in_)
if (p.storageClass & STC.in_)
buf.writestring("in ");
else if (p.storageClass & STC.out_)
buf.writestring("out ");
else if (p.storageClass & STC.lazy_)
buf.writestring("lazy ");
else if (p.storageClass & STC.alias_)
buf.writestring("alias ");

if (p.storageClass & STC.ref_)
buf.writestring("ref ");

StorageClass stc = p.storageClass;
if (p.type && p.type.mod & MODFlags.shared_)
stc &= ~STC.shared_;
Expand All @@ -3089,7 +3090,7 @@ private void parameterToBuffer(Parameter p, OutBuffer* buf, HdrGenState* hgs)
}
else
{
typeToBuffer(p.type, p.ident, buf, hgs);
typeToBuffer(p.type, p.ident, buf, hgs, (stc & STC.in_) ? MODFlags.const_ : 0);
}

if (p.defaultArg)
Expand Down Expand Up @@ -3220,14 +3221,15 @@ private void expToBuffer(Expression e, PREC pr, OutBuffer* buf, HdrGenState* hgs
/**************************************************
* An entry point to pretty-print type.
*/
private void typeToBuffer(Type t, const Identifier ident, OutBuffer* buf, HdrGenState* hgs)
private void typeToBuffer(Type t, const Identifier ident, OutBuffer* buf, HdrGenState* hgs,
ubyte modMask = 0)
{
if (auto tf = t.isTypeFunction())
{
visitFuncIdentWithPrefix(tf, ident, null, buf, hgs);
return;
}
visitWithMask(t, 0, buf, hgs);
visitWithMask(t, modMask, buf, hgs);
if (ident)
{
buf.writeByte(' ');
Expand Down
28 changes: 24 additions & 4 deletions src/dmd/mtype.d
Expand Up @@ -4440,6 +4440,10 @@ extern (C++) final class TypeFunction : TypeNext
if (!global.params.vsafe)
return stc;

// When the preview switch is enable, `in` parameters are `scope`
if (stc & STC.in_ && global.params.previewIn)
return stc | STC.scope_;

if (stc & (STC.scope_ | STC.return_ | STC.lazy_) || purity == PURE.impure)
return stc;

Expand Down Expand Up @@ -6829,19 +6833,35 @@ extern (C++) final class Parameter : ASTNode
/*********************************
* Compute covariance of parameters `this` and `p`
* as determined by the storage classes of both.
*
* Params:
* returnByRef = true if the function returns by ref
* p = Parameter to compare with
* previewIn = Whether `-previewIn` is being used, and thus if
* `in` means `scope`.
*
* Returns:
* true = `this` can be used in place of `p`
* false = nope
*/
bool isCovariant(bool returnByRef, const Parameter p) const pure nothrow @nogc @safe
bool isCovariant(bool returnByRef, const Parameter p, bool previewIn = global.params.previewIn)
const pure nothrow @nogc @safe
{
enum stc = STC.IOR | STC.lazy_;
if ((this.storageClass & stc) != (p.storageClass & stc))
ulong thisSTC = this.storageClass;
ulong otherSTC = p.storageClass;

if (previewIn)
{
if (thisSTC & STC.in_)
thisSTC |= STC.scope_;
if (otherSTC & STC.in_)
otherSTC |= STC.scope_;
}

enum stc = STC.ref_ | STC.out_ | STC.lazy_;
if ((thisSTC & stc) != (otherSTC & stc))
return false;
return isCovariantScope(returnByRef, this.storageClass, p.storageClass);
return isCovariantScope(returnByRef, thisSTC, otherSTC);
}

extern (D) private static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to) pure nothrow @nogc @safe
Expand Down
5 changes: 1 addition & 4 deletions src/dmd/typesem.d
Expand Up @@ -1261,9 +1261,6 @@ extern(C++) Type typeSemantic(Type t, const ref Loc loc, Scope* sc)
for (size_t i = 0; i < dim; i++)
{
Parameter fparam = tf.parameterList[i];
// If `-preview=in` is on, set `scope` as well
if ((fparam.storageClass & STC.in_) && global.params.inMeansScopeConst)
fparam.storageClass |= STC.scope_;
fparam.storageClass |= STC.parameter;
mtype.inuse++;
fparam.type = fparam.type.typeSemantic(loc, argsc);
Expand Down Expand Up @@ -1520,7 +1517,7 @@ extern(C++) Type typeSemantic(Type t, const ref Loc loc, Scope* sc)
}

// Remove redundant storage classes for type, they are already applied
fparam.storageClass &= ~(STC.TYPECTOR | STC.in_);
fparam.storageClass &= ~(STC.TYPECTOR);
}
argsc.pop();
}
Expand Down
Expand Up @@ -3,5 +3,5 @@

@safe:
void fun(in int* inParam);
static assert(__traits(getParameterStorageClasses, fun, 0)[0] == "scope");
static assert([__traits(getParameterStorageClasses, fun, 0)] == ["in"]);
static assert (is(typeof(fun) P == __parameters) && is(P[0] == const int*));
34 changes: 34 additions & 0 deletions test/fail_compilation/diagin.d
@@ -0,0 +1,34 @@
/*
PERMUTE_ARGS: -preview=in
TEST_OUTPUT:
---
fail_compilation/diagin.d(18): Error: function `diagin.foo(in string)` is not callable using argument types `()`
fail_compilation/diagin.d(18): missing argument for parameter #1: `in string`
fail_compilation/diagin.d(19): Error: function `diagin.foo1(in ref string)` is not callable using argument types `()`
fail_compilation/diagin.d(19): missing argument for parameter #1: `in ref string`
fail_compilation/diagin.d(20): Error: template `diagin.foo2` cannot deduce function from argument types `!()(int)`, candidates are:
fail_compilation/diagin.d(27): `foo2(T)(in T v, string)`
fail_compilation/diagin.d(22): Error: template `diagin.foo3` cannot deduce function from argument types `!()(bool[])`, candidates are:
fail_compilation/diagin.d(28): `foo3(T)(in ref T v, string)`
---
*/

void main ()
{
foo();
foo1();
foo2(42);
bool[] lvalue;
foo3(lvalue);
}

void foo(in string) {}
void foo1(in ref string) {}
void foo2(T)(in T v, string) {}
void foo3(T)(ref in T v, string) {}

// Ensure that `in` has a unique mangling
static assert(foo.mangleof == `_D6diagin3fooFIxAyaZv`);
static assert(foo1.mangleof == `_D6diagin4foo1FIKxAyaZv`);
static assert(foo2!int.mangleof == `_D6diagin__T4foo2TiZQiFNaNbNiNfIxiAyaZv`);
static assert(foo3!char.mangleof == `_D6diagin__T4foo3TaZQiFNaNbNiNfIKxaAyaZv`);
4 changes: 2 additions & 2 deletions test/fail_compilation/ice10922.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice10922.d(10): Error: function `ice10922.__lambda4(const(uint) n)` is not callable using argument types `()`
fail_compilation/ice10922.d(10): missing argument for parameter #1: `const(uint) n`
fail_compilation/ice10922.d(10): Error: function `ice10922.__lambda4(in uint n)` is not callable using argument types `()`
fail_compilation/ice10922.d(10): missing argument for parameter #1: `in uint n`
---
*/

Expand Down
2 changes: 1 addition & 1 deletion test/runnable/xtest46.d
Expand Up @@ -4963,7 +4963,7 @@ void test6763()
static assert(typeof(f6763).stringof == "void(int _param_0)");
static assert(typeof(c6763).stringof == "void(const(int) _param_0)");
static assert(typeof(r6763).stringof == "void(ref int _param_0)");
static assert(typeof(i6763).stringof == "void(const(int) _param_0)");
static assert(typeof(i6763).stringof == "void(in int _param_0)");
static assert(typeof(o6763).stringof == "void(out int _param_0)");
}

Expand Down