From 81af94b6941b454c96cedee82961a95dc3ea3a2d Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sun, 30 Nov 2025 23:54:46 -0800 Subject: [PATCH 1/2] implement final declarations --- compiler/src/dmd/dcast.d | 20 +++++++++++++++++++- compiler/src/dmd/dsymbolsem.d | 27 +++++++++++++++++---------- compiler/src/dmd/expressionsem.d | 11 ++++++++--- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/compiler/src/dmd/dcast.d b/compiler/src/dmd/dcast.d index 7ae2df768730..3c3474fcd1a0 100644 --- a/compiler/src/dmd/dcast.d +++ b/compiler/src/dmd/dcast.d @@ -17,6 +17,7 @@ import dmd.aggregate; import dmd.arrayop; import dmd.arraytypes; import dmd.astenums; +import dmd.printast; import dmd.dclass; import dmd.declaration; import dmd.denum; @@ -69,9 +70,26 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) { Expression visit(Expression e) { - //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); + printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); if (const match = (sc && sc.inCfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { + /* Do not allow taking a mutable pointer to a final + */ +printf("xyzzy\n"); +printAST(e); + SymOffExp es = e.isSymOffExp(); + if (es && es.var.storage_class & STC.final_) + { +printf("xyzzy2\n"); + Type tob = t.toBasetype(); + if (tob.ty == Tpointer && !tob.nextOf().isConst()) + { + error(e.loc, "cannot implicitly convert final `%s` to `%s`", es.toChars(), t.toChars()); + errorSupplemental(e.loc, "Note: a reference to a final can be done if it is const."); + return ErrorExp.get(); + } + } + // no need for an extra cast when matching is exact if (match == MATCH.convert && e.type.isTypeNoreturn() && e.op != EXP.type) diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index 5876705d1cc3..a5f7c6194b9d 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -2324,16 +2324,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor else if (dsym.type.isWild()) dsym.storage_class |= STC.wild; - if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_)) + if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_)) { - if (stc == STC.final_) - .error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars); - else - { - OutBuffer buf; - stcToBuffer(buf, stc); - .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars()); - } + OutBuffer buf; + stcToBuffer(buf, stc); + .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars()); dsym.storage_class &= ~stc; // strip off } @@ -2753,9 +2748,21 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { dsym.storage_class |= STC.nodtor; exp = exp.expressionSemantic(sc); +printf("dsymIsRef: ref %s = %s;\n", dsym.toChars(), exp.toChars()); Type tp = dsym.type; Type ta = exp.type; - if (!exp.isLvalue()) + if (!tp.isConst() && + exp.isVarExp() && + exp.isVarExp().var.storage_class & STC.final_) + { + .error(dsym.loc, "cannot take mutable ref to final variable `%s`, use `const ref`", exp.toChars()); + exp = ErrorExp.get(); + } + + if (exp.isErrorExp()) + { + } + else if (!exp.isLvalue()) { if (dsym.storage_class & STC.autoref) { diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index b3b425855f01..0f5968e86fc7 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -17305,7 +17305,7 @@ Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyF case EXP.variable: auto varExp = cast(VarExp)exp; - //printf("VarExp::checkModifiable %s", varExp.toChars()); + printf("VarExp::checkModifiable %s\n", varExp.toChars()); assert(varExp.type); return varExp.var.checkModify(varExp.loc, sc, null, flag); @@ -17499,7 +17499,12 @@ Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null Expression visitVar(VarExp exp) { - //printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars()); + printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars()); + if (exp.var.storage_class & STC.final_) + { + error(exp.loc, "cannot modify `final %s`", exp.toErrMsg()); + return ErrorExp.get(); + } if (exp.var.storage_class & STC.manifest) { error(exp.loc, "cannot modify manifest constant `%s`", exp.toErrMsg()); @@ -18264,7 +18269,7 @@ enum ModifyFlags */ private Modifiable checkModify(Declaration d, Loc loc, Scope* sc, Expression e1, ModifyFlags flag) { - //printf("checkModify() d: %s, e1: %s\n", d.toChars(), e1.toChars()); + printf("checkModify() d: %s, e1: %s, flag: %x\n", d.toChars(), e1 ? e1.toChars() : "null", flag); VarDeclaration v = d.isVarDeclaration(); if (v && v.canassign) return Modifiable.initialization; From 15ae04c8f655621e6bd41e9016dbc36234b3eaf7 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 1 Dec 2025 23:45:43 -0800 Subject: [PATCH 2/2] Implement Static Single Assignment (SSA) DIP --- compiler/src/dmd/dcast.d | 34 ++--- compiler/src/dmd/dsymbolsem.d | 30 ++--- compiler/src/dmd/expressionsem.d | 146 +++++++++++++++------ compiler/src/dmd/targetcompiler.d | 2 +- compiler/test/fail_compilation/fail170.d | 8 -- compiler/test/fail_compilation/fail179.d | 15 --- compiler/test/fail_compilation/fail180.d | 4 +- compiler/test/fail_compilation/failattr.d | 4 +- compiler/test/fail_compilation/testfinal.d | 55 ++++++++ 9 files changed, 196 insertions(+), 102 deletions(-) delete mode 100644 compiler/test/fail_compilation/fail170.d delete mode 100644 compiler/test/fail_compilation/fail179.d create mode 100644 compiler/test/fail_compilation/testfinal.d diff --git a/compiler/src/dmd/dcast.d b/compiler/src/dmd/dcast.d index 3c3474fcd1a0..666d17ea0bc3 100644 --- a/compiler/src/dmd/dcast.d +++ b/compiler/src/dmd/dcast.d @@ -17,7 +17,6 @@ import dmd.aggregate; import dmd.arrayop; import dmd.arraytypes; import dmd.astenums; -import dmd.printast; import dmd.dclass; import dmd.declaration; import dmd.denum; @@ -41,6 +40,7 @@ import dmd.intrange; import dmd.mtype; import dmd.opover; import dmd.optimize; +//import dmd.printast; import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.rmem; @@ -70,25 +70,25 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) { Expression visit(Expression e) { - printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); + //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); if (const match = (sc && sc.inCfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { - /* Do not allow taking a mutable pointer to a final - */ -printf("xyzzy\n"); -printAST(e); - SymOffExp es = e.isSymOffExp(); - if (es && es.var.storage_class & STC.final_) - { -printf("xyzzy2\n"); + /* Do not allow taking a mutable pointer to a final + */ + static if (0) // not sure this is needed + { + SymOffExp es = e.isSymOffExp(); + if (es && es.var.storage_class & STC.final_ && es.var.isVarDeclaration()) + { Type tob = t.toBasetype(); - if (tob.ty == Tpointer && !tob.nextOf().isConst()) - { - error(e.loc, "cannot implicitly convert final `%s` to `%s`", es.toChars(), t.toChars()); - errorSupplemental(e.loc, "Note: a reference to a final can be done if it is const."); - return ErrorExp.get(); - } - } + if (tob.ty == Tpointer && tob.nextOf().isMutable()) + { + error(e.loc, "cannot implicitly convert final `%s` to `%s`", es.toChars(), t.toChars()); + errorSupplemental(e.loc, "Note: a reference to a final can be done if it is const."); + return ErrorExp.get(); + } + } + } // no need for an extra cast when matching is exact diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index a5f7c6194b9d..c0238fb2ae89 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -2326,9 +2326,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_)) { - OutBuffer buf; - stcToBuffer(buf, stc); - .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars()); + OutBuffer buf; + stcToBuffer(buf, stc); + .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars()); dsym.storage_class &= ~stc; // strip off } @@ -2748,20 +2748,20 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { dsym.storage_class |= STC.nodtor; exp = exp.expressionSemantic(sc); -printf("dsymIsRef: ref %s = %s;\n", dsym.toChars(), exp.toChars()); Type tp = dsym.type; Type ta = exp.type; - if (!tp.isConst() && - exp.isVarExp() && - exp.isVarExp().var.storage_class & STC.final_) - { - .error(dsym.loc, "cannot take mutable ref to final variable `%s`, use `const ref`", exp.toChars()); - exp = ErrorExp.get(); - } - - if (exp.isErrorExp()) - { - } + if (tp.isMutable() && + exp.isVarExp() && + exp.isVarExp().var.isVarDeclaration() && + exp.isVarExp().var.storage_class & STC.final_) + { + .error(dsym.loc, "cannot take mutable ref to final variable `%s`, use `const ref`", exp.toChars()); + exp = ErrorExp.get(); + } + + if (exp.isErrorExp()) + { + } else if (!exp.isLvalue()) { if (dsym.storage_class & STC.autoref) diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 0f5968e86fc7..d03e931e80aa 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -3915,7 +3915,7 @@ private bool functionParameters(Loc loc, Scope* sc, //printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf)); assert(arguments); assert(fd || tf.next); - const size_t nparams = tf.parameterList.length; + const size_t nparams = tf.parameterList.length; // number of parameters const olderrors = global.errors; bool err = false; Expression eprefix = null; @@ -3937,7 +3937,7 @@ private bool functionParameters(Loc loc, Scope* sc, arguments.setDim(0); arguments.pushSlice((*resolvedArgs)[]); } - size_t nargs = arguments ? arguments.length : 0; + size_t nargs = arguments ? arguments.length : 0; // number of arguments if (nargs > nparams && tf.parameterList.varargs == VarArg.none) { @@ -3974,7 +3974,6 @@ private bool functionParameters(Loc loc, Scope* sc, const isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration(); - const size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) /* If the function return type has wildcards in it, we'll need to figure out the actual type * based on the actual argument types. @@ -3984,12 +3983,13 @@ private bool functionParameters(Loc loc, Scope* sc, MOD wildmatch = (tthis && !isCtorCall) ? tthis.Type.deduceWild(tf, false) : 0; bool done = false; + const size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) foreach (const i; 0 .. n) { Expression arg = (i < nargs) ? (*arguments)[i] : null; if (i >= nparams) - break; + break; // this loop is not for variadic arguments bool errorArgs() { @@ -3999,7 +3999,7 @@ private bool functionParameters(Loc loc, Scope* sc, Parameter p = tf.parameterList[i]; - if (!arg) + if (!arg) // the argument is missing, so use the default arg { if (!p.defaultArg) { @@ -4033,10 +4033,11 @@ private bool functionParameters(Loc loc, Scope* sc, if (MATCH m = arg.implicitConvTo(p.type)) { if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) - goto L2; + { } else if (nargs != nparams) return errorArgs(); - goto L1; + else + goto L1; } L2: Type tb = p.type.toBasetype(); @@ -4245,39 +4246,61 @@ private bool functionParameters(Loc loc, Scope* sc, // Look for mutable misaligned pointer, etc., in @safe mode err |= checkUnsafeAccess(sc, arg, false, true); } - else if (p.storageClass & STC.ref_) + else if (p.storageClass & (STC.ref_ | STC.out_)) { - if (sc.previews.rvalueRefParam && - !arg.isLvalue() && - targ.isCopyable()) - { /* allow rvalues to be passed to ref parameters by copying - * them to a temp, then pass the temp as the argument - */ - auto v = copyToTemp(STC.none, "__rvalue", arg); - Expression ev = new DeclarationExp(arg.loc, v); - ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v)); - arg = ev.expressionSemantic(sc); + if (VarExp ve = arg.isVarExp()) + { + if (ve.var.storage_class & STC.final_ && + ve.var.isVarDeclaration() && + p.type.isMutable() && + !(p.storageClass & STC.final_)) + { + /* func(ref int p); + final int ve = 3; + func(ve); // error + */ + const char* msg = p.storageClass & STC.ref_ + ? "cannot modify final `%s` with ref to mutable" + : "cannot pass final `%s` to `out` parameter"; + error(arg.loc, msg, ve.toErrMsg()); + err = true; + } } - arg = arg.toLvalue(sc, "create `ref` parameter from"); - // Look for mutable misaligned pointer, etc., in @safe mode - err |= checkUnsafeAccess(sc, arg, false, true); - } - else if (p.storageClass & STC.out_) - { - Type t = arg.type; - if (!t.isMutable() || !t.isAssignable()) // check blit assignable + if (p.storageClass & STC.ref_) { - error(arg.loc, "cannot modify struct `%s` with immutable members", arg.toChars()); - err = true; + if (sc.previews.rvalueRefParam && + !arg.isLvalue() && + targ.isCopyable()) + { /* allow rvalues to be passed to ref parameters by copying + * them to a temp, then pass the temp as the argument + */ + auto v = copyToTemp(STC.none, "__rvalue", arg); + Expression ev = new DeclarationExp(arg.loc, v); + ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v)); + arg = ev.expressionSemantic(sc); + } + arg = arg.toLvalue(sc, "create `ref` parameter from"); + + // Look for mutable misaligned pointer, etc., in @safe mode + err |= checkUnsafeAccess(sc, arg, false, true); } - else + else // STC.out_ { - // Look for misaligned pointer, etc., in @safe mode - err |= checkUnsafeAccess(sc, arg, false, true); - err |= checkDefCtor(arg.loc, t); // t must be default constructible + Type t = arg.type; + if (!t.isMutable() || !t.isAssignable()) // check blit assignable + { + error(arg.loc, "cannot modify struct `%s` with immutable members", arg.toChars()); + err = true; + } + else + { + // Look for misaligned pointer, etc., in @safe mode + err |= checkUnsafeAccess(sc, arg, false, true); + err |= checkDefCtor(arg.loc, t); // t must be default constructible + } + arg = arg.toLvalue(sc, "create `out` parameter from"); } - arg = arg.toLvalue(sc, "create `out` parameter from"); } else if (p.isLazy()) { @@ -10063,7 +10086,23 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - exp.type = exp.e1.type.pointerTo(); + /* Taking the address of a `final` variable means making a pointer to const + */ + bool toConst = false; + if (exp.e1.type.isMutable()) + { + if (VarExp ve = exp.e1.isVarExp()) + { + if (VarDeclaration v = ve.var.isVarDeclaration()) + { + if (v.storage_class & STC.final_) + toConst = true; + } + } + } + + exp.type = toConst ? exp.e1.type.constOf().pointerTo() + : exp.e1.type.pointerTo(); // See if this should really be a delegate if (exp.e1.op == EXP.dotVariable) @@ -10094,16 +10133,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else if (exp.e1.op == EXP.variable) { VarExp ve = cast(VarExp)exp.e1; - VarDeclaration v = ve.var.isVarDeclaration(); - if (v) + if (VarDeclaration v = ve.var.isVarDeclaration()) { if (!checkAddressVar(sc, exp.e1, v)) return setError(); v.checkPurity(ve.loc, sc); } - FuncDeclaration f = ve.var.isFuncDeclaration(); - if (f) + + if (FuncDeclaration f = ve.var.isFuncDeclaration()) { /* Because nested functions cannot be overloaded, * mark here that we took its address because castTo() @@ -17305,7 +17343,7 @@ Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyF case EXP.variable: auto varExp = cast(VarExp)exp; - printf("VarExp::checkModifiable %s\n", varExp.toChars()); + //printf("VarExp::checkModifiable %s\n", varExp.toChars()); assert(varExp.type); return varExp.var.checkModify(varExp.loc, sc, null, flag); @@ -17453,6 +17491,7 @@ Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null Expression visit(Expression exp) { //printf("Expression::modifiableLvalue() %s, type = %s\n", exp.toChars(), exp.type.toChars()); + //printAST(exp); // See if this expression is a modifiable lvalue (i.e. not const) if (exp.isBinAssignExp()) return exp.toLvalue(sc, "modify"); @@ -17499,7 +17538,7 @@ Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null Expression visitVar(VarExp exp) { - printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars()); + //printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars()); if (exp.var.storage_class & STC.final_) { error(exp.loc, "cannot modify `final %s`", exp.toErrMsg()); @@ -17534,6 +17573,30 @@ Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null return visit(exp); } + Expression visitIndex(IndexExp exp) + { + //printf("IndexExp::modifiableLvalue() %s, type %s\n", exp.toChars(), exp.type.toChars()); + auto e1 = exp.e1; + while (1) + { + if (auto e1x = e1.isIndexExp()) + { + e1 = e1x.e1; + continue; + } + break; + } + if (auto ve = e1.isVarExp()) + { + if (ve.type.isTypeSArray() && ve.var.storage_class & STC.final_) + { + error(exp.loc, "cannot modify `final %s`", exp.toErrMsg()); + return ErrorExp.get(); + } + } + return visit(exp); + } + Expression visitSlice(SliceExp exp) { error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toErrMsg()); @@ -17582,6 +17645,7 @@ Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null case EXP.string_: return visitString(_this.isStringExp()); case EXP.variable: return visitVar(_this.isVarExp()); case EXP.star: return visitPtr(_this.isPtrExp()); + case EXP.index: return visitIndex(_this.isIndexExp()); case EXP.slice: return visitSlice(_this.isSliceExp()); case EXP.comma: return visitComma(_this.isCommaExp()); case EXP.delegatePointer: return visitDelegatePtr(_this.isDelegatePtrExp()); @@ -18269,7 +18333,7 @@ enum ModifyFlags */ private Modifiable checkModify(Declaration d, Loc loc, Scope* sc, Expression e1, ModifyFlags flag) { - printf("checkModify() d: %s, e1: %s, flag: %x\n", d.toChars(), e1 ? e1.toChars() : "null", flag); + //printf("checkModify() d: %s, e1: %s, flag: %x\n", d.toChars(), e1 ? e1.toChars() : "null", flag); VarDeclaration v = d.isVarDeclaration(); if (v && v.canassign) return Modifiable.initialization; diff --git a/compiler/src/dmd/targetcompiler.d b/compiler/src/dmd/targetcompiler.d index 82e1ae50f0d2..8631c56d4ab4 100644 --- a/compiler/src/dmd/targetcompiler.d +++ b/compiler/src/dmd/targetcompiler.d @@ -84,7 +84,7 @@ else version (MARS) mixin template FuncDeclarationExtra() { VarDeclarations* alignSectionVars; /// local variables with alignment needs larger than stackAlign - import dmd.backend.cc : Symbol; + import dmd.backend.cc : Symbol; Symbol* salignSection; /// pointer to aligned section, if any } } diff --git a/compiler/test/fail_compilation/fail170.d b/compiler/test/fail_compilation/fail170.d deleted file mode 100644 index 61c7ae6b0ad6..000000000000 --- a/compiler/test/fail_compilation/fail170.d +++ /dev/null @@ -1,8 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail170.d(8): Error: variable `fail170.foo.x` cannot be `final`, perhaps you meant `const`? ---- -*/ - -void foo(final out int x) { } diff --git a/compiler/test/fail_compilation/fail179.d b/compiler/test/fail_compilation/fail179.d deleted file mode 100644 index 0c9c2489ab45..000000000000 --- a/compiler/test/fail_compilation/fail179.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail179.d(11): Error: variable `fail179.main.px` cannot be `final`, perhaps you meant `const`? ---- -*/ - -void main() -{ - int x = 3; - final px = &x; - *px = 4; - auto ppx = &px; - **ppx = 5; -} diff --git a/compiler/test/fail_compilation/fail180.d b/compiler/test/fail_compilation/fail180.d index ef4ffaa6088a..2055743a747a 100644 --- a/compiler/test/fail_compilation/fail180.d +++ b/compiler/test/fail_compilation/fail180.d @@ -5,11 +5,11 @@ fail_compilation/fail180.d(23): Error: cannot modify `this.x` in `const` functio fail_compilation/fail180.d(24): Error: cannot modify `this.x` in `const` function fail_compilation/fail180.d(38): Error: cannot modify `this.x` in `const` function fail_compilation/fail180.d(39): Error: cannot modify `this.x` in `const` function -fail_compilation/fail180.d(50): Error: variable `fail180.main.t` cannot be `final`, perhaps you meant `const`? -fail_compilation/fail180.d(62): Error: variable `fail180.test.d` cannot be `final`, perhaps you meant `const`? --- */ + + struct S59 { int x; diff --git a/compiler/test/fail_compilation/failattr.d b/compiler/test/fail_compilation/failattr.d index 012305af9564..dac277bedd17 100644 --- a/compiler/test/fail_compilation/failattr.d +++ b/compiler/test/fail_compilation/failattr.d @@ -6,9 +6,7 @@ TEST_OUTPUT: fail_compilation/failattr.d(103): Error: variable `failattr.C2901.v1` cannot be `synchronized` fail_compilation/failattr.d(104): Error: variable `failattr.C2901.v2` cannot be `override` fail_compilation/failattr.d(105): Error: variable `failattr.C2901.v3` cannot be `abstract` -fail_compilation/failattr.d(106): Error: variable `failattr.C2901.v4` cannot be `final`, perhaps you meant `const`? -fail_compilation/failattr.d(118): Error: variable `failattr.C2901.v13` cannot be `final abstract synchronized override` -fail_compilation/failattr.d(120): Error: variable `failattr.C2901.v14` cannot be `final`, perhaps you meant `const`? +fail_compilation/failattr.d(118): Error: variable `failattr.C2901.v13` cannot be `abstract synchronized override` fail_compilation/failattr.d(123): Error: undefined identifier `ERROR` --- */ diff --git a/compiler/test/fail_compilation/testfinal.d b/compiler/test/fail_compilation/testfinal.d new file mode 100644 index 000000000000..1a3c2732818e --- /dev/null +++ b/compiler/test/fail_compilation/testfinal.d @@ -0,0 +1,55 @@ +/* TEST_OUTPUT: +--- +fail_compilation/testfinal.d(26): Error: cannot take mutable ref to final variable `i`, use `const ref` +fail_compilation/testfinal.d(27): Error: cannot modify `final i` +fail_compilation/testfinal.d(28): Error: cannot implicitly convert `const(int)*` to `int*` +fail_compilation/testfinal.d(28): Note: Converting const to mutable requires an explicit cast (`cast(int*)`). +fail_compilation/testfinal.d(29): Error: cannot modify final `i` with ref to mutable +fail_compilation/testfinal.d(30): Error: cannot modify `final i` +fail_compilation/testfinal.d(31): Error: cannot pass final `i` to `out` parameter +fail_compilation/testfinal.d(51): Error: cannot modify `final tbuf[3]` +fail_compilation/testfinal.d(54): Error: cannot modify `final y[0][0]` +--- +*/ + +void legal() +{ + final int i = 3; + const ref r = i; + const(int)*p = &i; + pppp(i); +} + +void illegal() +{ + final int i = 3; + ref int r = i; + i = 4; + int* p = &i; + tttt(i); + ++i; + oooo(i); +} + +void tttt(ref int r); +void pppp(ref const int r); +void oooo(out int r); + +class C +{ + final int fo(); +} + +void legal2(C c) +{ + auto dg = &c.fo; +} + +void teststaticarray() +{ + final char[64] tbuf; + tbuf[3] = 'c'; + + final int[3][1] y = [[1,2,3]]; + y[0][0] = 2; +}