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

allow ref on locals, globals, and statics #16428

Merged
merged 2 commits into from
Jul 23, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions changelog/dmd.reflocal.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
`ref` can now be applied to local, static, extern, and global variables
thewilsonator marked this conversation as resolved.
Show resolved Hide resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
See the [DIP](https://github.com/WalterBright/documents/blob/master/varRef.md).

For example, one can now write:
```
struct S { int a; }

void main()
{
S s;
ref int r = s.a;
r = 3;
assert(s.a == 3);
}
```
WalterBright marked this conversation as resolved.
Show resolved Hide resolved
55 changes: 45 additions & 10 deletions compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import dmd.opover;
import dmd.optimize;
import dmd.parse;
debug import dmd.printast;
import dmd.root.array;
import dmd.root.filename;
import dmd.common.outbuffer;
Expand Down Expand Up @@ -1012,9 +1013,9 @@
}
}

if ((dsym.storage_class & (STC.ref_ | STC.parameter | STC.foreach_ | STC.temp | STC.result)) == STC.ref_ && dsym.ident != Id.This)
if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This)
{
.error(dsym.loc, "%s `%s` - only parameters, functions and `foreach` declarations can be `ref`", dsym.kind, dsym.toPrettyChars);
.error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars);

Check warning on line 1018 in compiler/src/dmd/dsymbolsem.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/dsymbolsem.d#L1018

Added line #L1018 was not covered by tests
}

if (dsym.type.hasWild())
Expand Down Expand Up @@ -1063,6 +1064,13 @@
}
}

bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_;
if (dsymIsRef)
{
if (!dsym._init && dsym.ident != Id.This)
.error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);

Check warning on line 1071 in compiler/src/dmd/dsymbolsem.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/dsymbolsem.d#L1071

Added line #L1071 was not covered by tests
}

FuncDeclaration fd = parent.isFuncDeclaration();
if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
{
Expand Down Expand Up @@ -1294,21 +1302,48 @@

Expression exp = ei.exp;
Expression e1 = new VarExp(dsym.loc, dsym);
if (isBlit)
exp = new BlitExp(dsym.loc, e1, exp);
if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach()
{
dsym.storage_class |= STC.nodtor;
exp = exp.expressionSemantic(sc);
Type tp = dsym.type;
Type ta = exp.type;
if (!exp.isLvalue())
{
.error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
exp = ErrorExp.get();

Check warning on line 1314 in compiler/src/dmd/dsymbolsem.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/dsymbolsem.d#L1313-L1314

Added lines #L1313 - L1314 were not covered by tests
}
else if (!ta.constConv(tp))
{
.error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
exp = ErrorExp.get();

Check warning on line 1319 in compiler/src/dmd/dsymbolsem.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/dsymbolsem.d#L1318-L1319

Added lines #L1318 - L1319 were not covered by tests
}
else
{
exp = new ConstructExp(dsym.loc, e1, exp);
dsym.canassign++;
exp = exp.expressionSemantic(sc);
dsym.canassign--;
}
}
else
exp = new ConstructExp(dsym.loc, e1, exp);
dsym.canassign++;
exp = exp.expressionSemantic(sc);
dsym.canassign--;
exp = exp.optimize(WANTvalue);
{
if (isBlit)
exp = new BlitExp(dsym.loc, e1, exp);
else
exp = new ConstructExp(dsym.loc, e1, exp);
dsym.canassign++;
exp = exp.expressionSemantic(sc);
dsym.canassign--;
}

if (exp.op == EXP.error)
{
dsym._init = new ErrorInitializer();
ei = null;
}
else
ei.exp = exp;
ei.exp = exp.optimize(WANTvalue);
}
else
{
Expand Down
66 changes: 63 additions & 3 deletions compiler/test/fail_compilation/diag9679.d
Original file line number Diff line number Diff line change
@@ -1,13 +1,73 @@
/*
/* REQUIRED_ARGS: -verrors=0
TEST_OUTPUT:
---
fail_compilation/diag9679.d(11): Error: variable `diag9679.main.n` - only parameters, functions and `foreach` declarations can be `ref`
fail_compilation/diag9679.d(12): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?
fail_compilation/diag9679.d(15): Error: rvalue `1` cannot be assigned to `ref n`
fail_compilation/diag9679.d(16): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?
fail_compilation/diag9679.d(17): Error: variable `diag9679.main.S.a` - field declarations cannot be `ref`
fail_compilation/diag9679.d(24): Error: returning `r` escapes a reference to local variable `i`
---
*/



void main()
{
if (ref n = 1) {}
if (auto int n = 1) {}
struct S { ref int a; }
}

ref int test2()
{
int i;
ref r = i;
return r;
}

ref int test3()
{
extern int i;
ref r = i;
return r;
}

struct S { int a; }

void test4()
{
S s;
ref int r1 = s.a;
r1 = 3;
__gshared S t2;
ref int r2 = t2.a;
static S t3;
ref int r3 = t3.a;
extern S t4;
ref int r4 = t4.a;
}

/* TEST_OUTPUT:
---
fail_compilation/diag9679.d(60): Error: variable `diag9679.test5.r5` - initializer is required for `ref` variable
fail_compilation/diag9679.d(60): Error: rvalue `0` cannot be assigned to `ref r5`
fail_compilation/diag9679.d(65): Error: rvalue `4` cannot be assigned to `ref x`
fail_compilation/diag9679.d(66): Error: returning `x` escapes a reference to local variable `x`
fail_compilation/diag9679.d(71): Error: type `immutable(int)` cannot be assigned to `ref int x`
---
*/
void test5()
{
ref int r5;
}

ref int test6()
{
ref int x = 4;
return x;
}

void test7(immutable int y)
{
ref int x = y;
x = 5;
}
29 changes: 29 additions & 0 deletions compiler/test/runnable/declaration.d
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,33 @@ void test13950()

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


void testlocalref()
{
int x = 4;
ref int rx = x;
rx = 5;
assert(x == 5);
ref int r2 = rx;
r2 = 6;
assert(x == 6);
}

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

int global;

ref int tgr() { return global; }

void testglobalref()
{
auto i = tgr();
i = 1;
assert(global == 0);
}

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

int main()
{
test6475();
Expand All @@ -419,6 +446,8 @@ int main()
test10142();
test11421();
test13950();
testlocalref();
testglobalref();

printf("Success\n");
return 0;
Expand Down
Loading