Skip to content
Open
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
18 changes: 18 additions & 0 deletions compiler/src/dmd/dcast.d
Original file line number Diff line number Diff line change
Expand Up @@ -40,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;
Expand Down Expand Up @@ -72,6 +73,23 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t)
//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
*/
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().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

if (match == MATCH.convert && e.type.isTypeNoreturn() && e.op != EXP.type)
Expand Down
27 changes: 17 additions & 10 deletions compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -2755,7 +2750,19 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
exp = exp.expressionSemantic(sc);
Type tp = dsym.type;
Type ta = exp.type;
if (!exp.isLvalue())
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)
{
Expand Down
149 changes: 109 additions & 40 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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.
Expand All @@ -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()
{
Expand All @@ -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)
{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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())
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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", varExp.toChars());
//printf("VarExp::checkModifiable %s\n", varExp.toChars());
assert(varExp.type);
return varExp.var.checkModify(varExp.loc, sc, null, flag);

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -17500,6 +17539,11 @@ Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null
Expression visitVar(VarExp exp)
{
//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());
Expand Down Expand Up @@ -17529,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());
Expand Down Expand Up @@ -17577,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());
Expand Down Expand Up @@ -18264,7 +18333,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;
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/targetcompiler.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
8 changes: 0 additions & 8 deletions compiler/test/fail_compilation/fail170.d

This file was deleted.

15 changes: 0 additions & 15 deletions compiler/test/fail_compilation/fail179.d

This file was deleted.

4 changes: 2 additions & 2 deletions compiler/test/fail_compilation/fail180.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading
Loading