Skip to content

Commit

Permalink
Tighten covariance rules wrt. in params with -preview=in
Browse files Browse the repository at this point in the history
Require the `in` storage class on both sides, just like ref/out/lazy.

Doing so makes sure the code compiles with all compilers and for all
targets, independent from whether `in` means `const scope` or `const
scope ref`.
  • Loading branch information
kinke committed Oct 7, 2020
1 parent f518a22 commit ccd0dd9
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 58 deletions.
8 changes: 4 additions & 4 deletions src/dmd/mtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -6900,8 +6900,8 @@ extern (C++) final class Parameter : ASTNode
* 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`.
* previewIn = Whether `-preview=in` is being used, and thus if
* `in` means `scope [ref]`.
*
* Returns:
* true = `this` can be used in place of `p`
Expand All @@ -6921,8 +6921,8 @@ extern (C++) final class Parameter : ASTNode
otherSTC |= STC.scope_;
}

enum stc = STC.ref_ | STC.out_ | STC.lazy_;
if ((thisSTC & stc) != (otherSTC & stc))
const mask = STC.ref_ | STC.out_ | STC.lazy_ | (previewIn ? STC.in_ : 0);
if ((thisSTC & mask) != (otherSTC & mask))
return false;
return isCovariantScope(returnByRef, thisSTC, otherSTC);
}
Expand Down
2 changes: 1 addition & 1 deletion src/dmd/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ class Parameter : public ASTNode
static size_t dim(Parameters *parameters);
static Parameter *getNth(Parameters *parameters, d_size_t nth);
const char *toChars() const;
bool isCovariant(bool returnByRef, const Parameter *p) const;
bool isCovariant(bool returnByRef, const Parameter *p, bool previewIn) const;
};

struct ParameterList
Expand Down
11 changes: 5 additions & 6 deletions test/compilable/previewin.d
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ struct FooBar
string toString() const
{
string result;
// Type is const
this.toString((in char[] buf) {
static assert(is(typeof(buf) == const(char[])));
result ~= buf;
});
// Type inference works
this.toString((buf) { result ~= buf; });
// Specifying the STC too
this.toString((in buf) { result ~= buf; });
// Some covariance
this.toString((const scope buf) { result ~= buf; });
this.toString((scope const(char)[] buf) { result ~= buf; });
this.toString((scope const(char[]) buf) { result ~= buf; });
return result;
}

Expand Down
8 changes: 4 additions & 4 deletions test/fail_compilation/diagin.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
PERMUTE_ARGS: -preview=in
TEST_OUTPUT:
---
fail_compilation/diagin.d(14): Error: function `diagin.foo(in string)` is not callable using argument types `()`
fail_compilation/diagin.d(14): missing argument for parameter #1: `in string`
fail_compilation/diagin.d(14): Error: function `diagin.foo(in int)` is not callable using argument types `()`
fail_compilation/diagin.d(14): missing argument for parameter #1: `in int`
fail_compilation/diagin.d(16): Error: template `diagin.foo1` cannot deduce function from argument types `!()(bool[])`, candidates are:
fail_compilation/diagin.d(20): `foo1(T)(in T v, string)`
---
Expand All @@ -16,10 +16,10 @@ void main ()
foo1(lvalue);
}

void foo(in string) {}
void foo(in int) {}
void foo1(T)(in T v, string) {}

// Ensure that `in` has a unique mangling
static assert(foo.mangleof == `_D6diagin3fooFIAyaZv`);
static assert(foo.mangleof == `_D6diagin3fooFIiZv`);
static assert(foo1!int.mangleof == `_D6diagin__T4foo1TiZQiFNaNbNiNfIiAyaZv`);
static assert(foo1!char.mangleof == `_D6diagin__T4foo1TaZQiFNaNbNiNfIaAyaZv`);
60 changes: 17 additions & 43 deletions test/fail_compilation/previewin.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,33 @@
REQUIRED_ARGS: -preview=in -preview=dip1000
TEST_OUTPUT:
---
fail_compilation/previewin.d(3): Error: function `previewin.func1(void function(ulong[8]) dg)` is not callable using argument types `(void function(in ulong[8]))`
fail_compilation/previewin.d(3): cannot pass argument `& func_byRef` of type `void function(in ulong[8])` to parameter `void function(ulong[8]) dg`
fail_compilation/previewin.d(4): Error: function `previewin.func2(void function(ref ulong[8]) dg)` is not callable using argument types `(void function(in ulong[8]))`
fail_compilation/previewin.d(4): cannot pass argument `& func_byRef` of type `void function(in ulong[8])` to parameter `void function(ref ulong[8]) dg`
fail_compilation/previewin.d(7): Error: function `previewin.func4(void function(ref uint) dg)` is not callable using argument types `(void function(in uint))`
fail_compilation/previewin.d(7): cannot pass argument `& func_byValue` of type `void function(in uint)` to parameter `void function(ref uint) dg`
fail_compilation/previewin.d(41): Error: scope variable `arg` assigned to non-scope `myGlobal`
fail_compilation/previewin.d(42): Error: scope variable `arg` assigned to non-scope `myGlobal`
fail_compilation/previewin.d(43): Error: scope variable `arg` may not be returned
fail_compilation/previewin.d(44): Error: scope variable `arg` assigned to `escape` with longer lifetime
fail_compilation/previewin.d(48): Error: returning `arg` escapes a reference to parameter `arg`
fail_compilation/previewin.d(48): perhaps annotate the parameter with `return`
fail_compilation/previewin.d(4): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)`
fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(const(real) x) pure nothrow @nogc @safe)`
fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref const(real) x) pure nothrow @nogc @safe)`
fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to non-scope `myGlobal`
fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to non-scope `myGlobal`
fail_compilation/previewin.d(17): Error: scope variable `arg` may not be returned
fail_compilation/previewin.d(18): Error: scope variable `arg` assigned to `escape` with longer lifetime
fail_compilation/previewin.d(22): Error: returning `arg` escapes a reference to parameter `arg`
fail_compilation/previewin.d(22): perhaps annotate the parameter with `return`
---
*/

#line 1
void main ()
{
func1(&func_byRef); // No
func2(&func_byRef); // No
func3(&func_byRef); // Could be Yes, but currently No

func4(&func_byValue); // No
func5(&func_byValue); // Yes

func6(&func_byValue2); // Yes
func7(&func_byValue3); // Yes
// No covariance without explicit `in`
takeFunction((real x) {});
takeFunction((const scope real x) {});
takeFunction((const scope ref real x) {});

tryEscape("Hello World"); // Yes by `tryEscape` is NG
}

// Takes by `scope ref const`
void func_byRef(in ulong[8]) {}
// Takes by `scope const`
void func_byValue(in uint) {}

// Error: `ulong[8]` is passed by `ref`
void func1(void function(scope ulong[8]) dg) {}
// Error: Missing `scope` on a `ref`
void func2(void function(ref ulong[8]) dg) {}
// Works: `scope ref`
void func3(void function(scope const ref ulong[8]) dg) {}

// Error: `uint` is passed by value
void func4(void function(ref uint) dg) {}
// Works: By value `scope const`
void func5(void function(scope const uint) dg) {}

// This works for arrays:
void func_byValue2(in char[]) {}
void func6(void function(char[]) dg) {}
void func_byValue3(scope const(char)[]) {}
void func7(void function(in char[]) dg) {}
void takeFunction(void function(in real) f);

// Make sure things cannot be escaped (`scope` is applied)
const(char)[] myGlobal;
Expand Down
1 change: 1 addition & 0 deletions test/runnable/previewin.d
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); }

// By ref because of non-copyability
void testin5(in NonCopyable noncopy) { static assert(__traits(isRef, noncopy)); }
static assert(testin5.mangleof == "_D9previewin7testin5FNaNbNiNfIKSQBe11NonCopyableZv"); // incl. `ref`
// By ref because of postblit
void testin6(in WithPostblit withpostblit) { static assert(__traits(isRef, withpostblit)); }
// By ref because of copy ctor
Expand Down

0 comments on commit ccd0dd9

Please sign in to comment.