diff --git a/changelog/dmd.assert-string.dd b/changelog/dmd.assert-string.dd new file mode 100644 index 0000000000..19f74b9038 --- /dev/null +++ b/changelog/dmd.assert-string.dd @@ -0,0 +1,18 @@ +A string literal as an assert condition is deprecated + +Boolean evaluation of a string literal could happen unintentionally +e.g. when an `assert(0, "message")` was meant and the `0` was missing. + +```d +assert("unexpected runtime condition"); +static assert("unhandled case for `", T, "`"); +``` + +The 2 asserts would silently always have no effect. +Now these cases will be detected with deprecation messages. +If the original behaviour was actually intended, use `expr !is null` instead: + +```d +assert("" !is null); +static assert("" !is null); +``` diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 13c9ceea48..8a01644d14 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -7592,6 +7592,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AssertExp::semantic('%s')\n", exp.toChars()); } + if (auto e = exp.e1.isStringExp()) + { + // deprecated in 2.107 + deprecation(e.loc, "assert condition cannot be a string literal"); + deprecationSupplemental(e.loc, "If intentional, use `%s !is null` instead to preserve behaviour", + e.toChars()); + } const generateMsg = !exp.msg && sc.needsCodegen() && // let ctfe interpreter handle the error message diff --git a/compiler/src/dmd/semantic2.d b/compiler/src/dmd/semantic2.d index 036560b540..292cf62c59 100644 --- a/compiler/src/dmd/semantic2.d +++ b/compiler/src/dmd/semantic2.d @@ -93,6 +93,13 @@ private extern(C++) final class Semantic2Visitor : Visitor override void visit(StaticAssert sa) { //printf("StaticAssert::semantic2() %s\n", sa.toChars()); + if (const e = sa.exp.isStringExp()) + { + // deprecated in 2.107 + deprecation(e.loc, "static assert condition cannot be a string literal"); + deprecationSupplemental(e.loc, "If intentional, use `%s !is null` instead to preserve behaviour", + e.toChars()); + } auto sds = new ScopeDsymbol(); sc = sc.push(sds); sc.tinst = null; diff --git a/compiler/test/fail_compilation/array_bool.d b/compiler/test/fail_compilation/array_bool.d new file mode 100644 index 0000000000..923766a93f --- /dev/null +++ b/compiler/test/fail_compilation/array_bool.d @@ -0,0 +1,22 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/array_bool.d(13): Deprecation: assert condition cannot be a string literal +fail_compilation/array_bool.d(13): If intentional, use `"foo" !is null` instead to preserve behaviour +fail_compilation/array_bool.d(14): Deprecation: static assert condition cannot be a string literal +fail_compilation/array_bool.d(14): If intentional, use `"foo" !is null` instead to preserve behaviour +--- +*/ +void main() +{ + assert("foo"); + static assert("foo"); + + assert("foo".ptr); // OK + static assert("foo".ptr); // OK + + enum e = "bar"; + static assert(e); // OK + assert(e); // OK +} diff --git a/compiler/test/runnable/testpdb.d b/compiler/test/runnable/testpdb.d index 55207c708e..8a1b10dfb9 100644 --- a/compiler/test/runnable/testpdb.d +++ b/compiler/test/runnable/testpdb.d @@ -1087,7 +1087,7 @@ bool openDebugInfo(IDiaDataSource* source, IDiaSession* session, IDiaSymbol* glo { wchar[MAX_PATH] exepath; DWORD len = GetModuleFileNameW(null, exepath.ptr, MAX_PATH); - len < MAX_PATH || assert("executable path too long"); + len < MAX_PATH || assert(false, "executable path too long"); HRESULT hr = CoInitialize(NULL);