From 28bc5c6e7436b6b30b3bac1c31b406bd274f4575 Mon Sep 17 00:00:00 2001 From: Dennis Date: Wed, 29 Jan 2025 11:26:01 +0100 Subject: [PATCH] Fix #20763 - Inconsistent handling of type + value in typeof expressions (#20798) * Fix #20763 - Inconsistent handling of type + value in typeof expressions * Add supplemental error --------- Co-authored-by: Dennis Korpel --- compiler/src/dmd/expressionsem.d | 15 ++++++++--- compiler/src/dmd/typesem.d | 12 +++++++++ compiler/test/fail_compilation/b17285.d | 7 ++--- compiler/test/fail_compilation/ice9545.d | 3 ++- compiler/test/fail_compilation/test20763.d | 31 ++++++++++++++++++++++ 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 compiler/test/fail_compilation/test20763.d diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 394bb2d9f423..3b9743e2927d 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -14769,11 +14769,16 @@ private bool checkArithmetic(Expression e, EXP op) return true; } - // FIXME: Existing code relies on adding / subtracting types in typeof() expressions: - // alias I = ulong; alias U = typeof(I + 1u); - // https://github.com/dlang/dmd/issues/20763 - if (op == EXP.add || op == EXP.min) + if ((op == EXP.add || op == EXP.min) && e.isTypeExp()) + { + // @@@DEPRECATED_2.121@@@ + // Deprecated in 2.111 + // In 2.121, remove this branch to let `checkValue` raise the error + deprecation(e.loc, "type `%s` has no value", e.toChars); + if (!e.type.isOpaqueType) + deprecationSupplemental(e.loc, "perhaps use `%s.init`", e.toChars); return false; + } return e.checkValue(); } @@ -14844,6 +14849,8 @@ bool checkValue(Expression e) if (auto te = e.isTypeExp()) { error(e.loc, "type `%s` has no value", e.toChars()); + if (!e.type.isOpaqueType) + errorSupplemental(e.loc, "perhaps use `%s.init`", e.toChars()); return true; } diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d index 073f0906494b..6d21d67befc5 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -7609,6 +7609,18 @@ bool checkRetType(TypeFunction tf, const ref Loc loc) return false; } +/// Returns: whether `t` is a struct/class/enum without a body +bool isOpaqueType(Type t) +{ + if (auto te = t.isTypeEnum()) + return te.sym.members is null; + if (auto ts = t.isTypeStruct()) + return ts.sym.members is null; + if (auto tc = t.isTypeClass()) + return tc.sym.members is null; + return false; +} + /******************************* Private *****************************************/ diff --git a/compiler/test/fail_compilation/b17285.d b/compiler/test/fail_compilation/b17285.d index 7b79cf0666fc..9bf5d96cb247 100644 --- a/compiler/test/fail_compilation/b17285.d +++ b/compiler/test/fail_compilation/b17285.d @@ -1,9 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/b17285.d(14): Error: type `ONE` has no value -fail_compilation/b17285.d(14): Error: type `TWO` has no value -fail_compilation/b17285.d(14): Error: cannot implicitly convert expression `ONE` of type `b17285.ONE` to `int` +fail_compilation/b17285.d(15): Error: type `ONE` has no value +fail_compilation/b17285.d(15): perhaps use `ONE.init` +fail_compilation/b17285.d(15): Error: type `TWO` has no value +fail_compilation/b17285.d(15): Error: cannot implicitly convert expression `ONE` of type `b17285.ONE` to `int` --- */ diff --git a/compiler/test/fail_compilation/ice9545.d b/compiler/test/fail_compilation/ice9545.d index 7360bc534f2a..b10e63987cf1 100644 --- a/compiler/test/fail_compilation/ice9545.d +++ b/compiler/test/fail_compilation/ice9545.d @@ -2,7 +2,8 @@ /* TEST_OUTPUT: ---- -fail_compilation/ice9545.d(13): Error: type `int` has no value +fail_compilation/ice9545.d(14): Error: type `int` has no value +fail_compilation/ice9545.d(14): perhaps use `int.init` ---- */ diff --git a/compiler/test/fail_compilation/test20763.d b/compiler/test/fail_compilation/test20763.d new file mode 100644 index 000000000000..786f8d895abd --- /dev/null +++ b/compiler/test/fail_compilation/test20763.d @@ -0,0 +1,31 @@ +/* +REQUIRED_ARGS: -de + +TEST_OUTPUT: +--- +fail_compilation/test20763.d(25): Deprecation: type `ulong` has no value +fail_compilation/test20763.d(25): perhaps use `ulong.init` +fail_compilation/test20763.d(26): Deprecation: type `ulong` has no value +fail_compilation/test20763.d(26): perhaps use `ulong.init` +fail_compilation/test20763.d(27): Error: type `ulong` has no value +fail_compilation/test20763.d(27): perhaps use `ulong.init` +fail_compilation/test20763.d(28): Error: type `ulong` has no value +fail_compilation/test20763.d(28): perhaps use `ulong.init` +fail_compilation/test20763.d(29): Error: type `ulong` has no value +fail_compilation/test20763.d(29): perhaps use `ulong.init` +fail_compilation/test20763.d(30): Error: type `ulong` has no value +fail_compilation/test20763.d(30): perhaps use `ulong.init` +--- +*/ + +// https://github.com/dlang/dmd/issues/20763 +void test() +{ + alias I = ulong; + alias U0 = typeof(I + 1u); + alias U1 = typeof(1 - I); + alias U2 = typeof(+I); + alias U3 = typeof(I * 1); + alias U4 = typeof(I << 1); + alias U5 = typeof(I | 1); +}