diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index 479ad3ade5f3..a26f3ab83b99 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -3824,6 +3824,7 @@ extern (C++) final class CastExp : UnaExp { Type to; // type to cast to ubyte mod = cast(ubyte)~0; // MODxxxxx + bool trusted; // assume cast is safe extern (D) this(const ref Loc loc, Expression e, Type t) @safe { diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index c08e2cd7b29e..442c0d9e8be6 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -11864,7 +11864,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (checkNewEscape(sc, exp.e2, false)) return setError(); - exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, tb1next)); + auto ecast = exp.e2.castTo(sc, tb1next); + if (auto ce = ecast.isCastExp()) + ce.trusted = true; + + exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast); exp.e2 = doCopyOrMove(sc, exp.e2); } else if (tb1.ty == Tarray && @@ -15732,7 +15736,15 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action return visit(_this); } if (_this.isLvalue()) + { + with (_this) + if (!trusted && !e1.type.pointerTo().implicitConvTo(to.pointerTo())) + sc.setUnsafePreview(FeatureState.default_, false, loc, + "cast from `%s` to `%s` cannot be used as an lvalue in @safe code", + e1.type, to); + return _this; + } return visit(_this); } diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 923f47506fee..fe0008c8d161 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -2425,6 +2425,7 @@ class CastExp final : public UnaExp public: Type* to; uint8_t mod; + bool trusted; CastExp* syntaxCopy() override; bool isLvalue() override; void accept(Visitor* v) override; diff --git a/compiler/test/fail_compilation/cast_qual.d b/compiler/test/fail_compilation/cast_qual.d new file mode 100644 index 000000000000..19932f811653 --- /dev/null +++ b/compiler/test/fail_compilation/cast_qual.d @@ -0,0 +1,19 @@ +/* +REQUIRED_ARGS: -preview=dip1000 -de +TEST_OUTPUT: +--- +fail_compilation/cast_qual.d(15): Deprecation: cast from `const(int)` to `int` cannot be used as an lvalue in @safe code +fail_compilation/cast_qual.d(17): Deprecation: cast from `const(int)` to `int` cannot be used as an lvalue in @safe code +--- +*/ + +@safe: + +void main() { + const int i = 3; + int j = cast() i; // OK + int* p = &cast() i; // this should not compile in @safe code + *p = 4; // oops + cast() i = 5; // NG + auto q = &cast(const) j; // OK, int* to const int* +} diff --git a/druntime/src/core/sync/mutex.d b/druntime/src/core/sync/mutex.d index e7380c467254..5f547cd04ef6 100644 --- a/druntime/src/core/sync/mutex.d +++ b/druntime/src/core/sync/mutex.d @@ -317,7 +317,7 @@ unittest cargo = 42; } - void useResource() shared @safe nothrow @nogc + void useResource() shared @trusted nothrow @nogc { mtx.lock_nothrow(); (cast() cargo) += 1;