Implement auto ref for local variables

This commit is contained in:
Dennis Korpel 2024-08-05 23:28:52 +02:00 committed by The Dlang Bot
parent b02f718380
commit f7f320a8e4
3 changed files with 117 additions and 32 deletions

View file

@ -615,6 +615,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (ad)
dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
if ((dsym.storage_class & STC.auto_) && (dsym.storage_class & STC.ref_))
dsym.storage_class |= STC.autoref;
/* If auto type inference, do the inference
*/
int inferred = 0;
@ -732,7 +735,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
dsym.type = Type.terror;
}
}
if ((dsym.storage_class & STC.auto_) && !inferred)
if ((dsym.storage_class & STC.auto_) && !inferred && !(dsym.storage_class & STC.autoref))
.error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars);
if (auto tt = tb.isTypeTuple())
@ -1069,8 +1072,20 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (dsymIsRef)
{
if (!dsym._init && dsym.ident != Id.This)
{
if (dsym.storage_class & STC.autoref)
{
dsymIsRef = false;
dsym.storage_class &= ~STC.ref_;
}
else
.error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);
}
else if (dsym._init.isVoidInitializer())
{
.error(dsym.loc, "%s `%s` - void initializer not allowed for `ref` variable", dsym.kind, dsym.toPrettyChars);
}
}
FuncDeclaration fd = parent.isFuncDeclaration();
if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
@ -1303,31 +1318,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
Expression exp = ei.exp;
Expression e1 = new VarExp(dsym.loc, dsym);
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();
}
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();
}
else
{
exp = new ConstructExp(dsym.loc, e1, exp);
dsym.canassign++;
exp = exp.expressionSemantic(sc);
dsym.canassign--;
}
}
else
void constructInit(bool isBlit)
{
if (isBlit)
exp = new BlitExp(dsym.loc, e1, exp);
@ -1338,6 +1330,48 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
dsym.canassign--;
}
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())
{
if (dsym.storage_class & STC.autoref)
{
dsym.storage_class &= ~STC.ref_;
constructInit(isBlit);
}
else
{
.error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
exp = ErrorExp.get();
}
}
else if (!ta.constConv(tp))
{
if (dsym.storage_class & STC.autoref)
{
dsym.storage_class &= ~STC.ref_;
constructInit(false);
}
else
{
.error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
exp = ErrorExp.get();
}
}
else
{
constructInit(false);
}
}
else
{
constructInit(isBlit);
}
if (exp.op == EXP.error)
{
dsym._init = new ErrorInitializer();

View file

@ -48,13 +48,19 @@ void test4()
/* 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`
fail_compilation/diag9679.d(66): Error: variable `diag9679.test5.r5` - initializer is required for `ref` variable
fail_compilation/diag9679.d(66): Error: rvalue `0` cannot be assigned to `ref r5`
fail_compilation/diag9679.d(71): Error: rvalue `4` cannot be assigned to `ref x`
fail_compilation/diag9679.d(72): Error: returning `x` escapes a reference to local variable `x`
fail_compilation/diag9679.d(77): Error: type `immutable(int)` cannot be assigned to `ref int x`
fail_compilation/diag9679.d(84): Error: returning `x` escapes a reference to local variable `x`
fail_compilation/diag9679.d(89): Error: variable `diag9679.test9.x` - void initializer not allowed for `ref` variable
fail_compilation/diag9679.d(90): Error: variable `diag9679.test9.y` - void initializer not allowed for `ref` variable
---
*/
void test5()
{
ref int r5;
@ -71,3 +77,15 @@ void test7(immutable int y)
ref int x = y;
x = 5;
}
ref int test8()
{
auto ref int x = 3;
return x;
}
void test9()
{
ref int x = void;
auto ref int y = void;
}

View file

@ -433,6 +433,38 @@ void testglobalref()
/***************************************************/
void testAutoRef()
{
auto ref int x = 4;
auto ref int y = x;
auto ref z = x + 0;
auto ref char w;
auto ref float v = x; // type conversion
static assert(!__traits(isRef, x));
static assert( __traits(isRef, y));
static assert(!__traits(isRef, z));
static assert(!__traits(isRef, w));
static assert(!__traits(isRef, v));
assert(&y == &x);
assert(&z != &x);
assert(w == char.init);
assert(v == 4.0);
if (auto ref int a = 3)
static assert(!__traits(isRef, a));
while (auto ref a = x)
{
static assert(is(typeof(a) == int));
static assert(__traits(isRef, a));
break;
}
}
/***************************************************/
int main()
{
test6475();
@ -448,6 +480,7 @@ int main()
test13950();
testlocalref();
testglobalref();
testAutoRef();
printf("Success\n");
return 0;