[clang] [clang] fix constexpr-unknown handling of self-references. (PR #132990)

Eli Friedman via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 26 12:05:34 PDT 2025


================
@@ -177,3 +177,50 @@ namespace extern_reference_used_as_unknown {
   int y;
   constinit int& g = (x,y); // expected-warning {{left operand of comma operator has no effect}}
 }
+
+namespace uninit_reference_used {
+  int y;
+  constexpr int &r = r; // expected-error {{must be initialized by a constant expression}} \
+  // nointerpreter-note {{initializer of 'r' is not a constant expression}} \
+  // nointerpreter-note {{declared here}}
+  constexpr int &rr = (rr, y);
+  constexpr int &g() {
+    int &x = x; // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
+    // nointerpreter-note {{declared here}} \
+    // interpreter-note {{read of uninitialized object is not allowed in a constant expression}}
+    return x;
+  }
+  constexpr int &gg = g(); // expected-error {{must be initialized by a constant expression}} \
+  // nointerpreter-note {{reference to 'x' is not a constant expression}} \
+  // interpreter-note {{in call to 'g()'}}
+  constexpr int g2() {
+    int &x = x; // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
+    // interpreter-note {{read of uninitialized object is not allowed in a constant expression}}
+    return x;
+  }
+  constexpr int gg2 = g2(); // expected-error {{must be initialized by a constant expression}} \
+  // interpreter-note {{in call to 'g2()'}}
+  constexpr int &g3() {
+    int &x = (x,y); // expected-warning{{left operand of comma operator has no effect}} \
+    // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}}
+    return x;
+  }
+  constexpr int &gg3 = g3();
+  typedef decltype(sizeof(1)) uintptr_t;
+  constexpr uintptr_t g4() {
+    uintptr_t * &x = x; // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
+    // interpreter-note {{read of uninitialized object is not allowed in a constant expression}}
+    *(uintptr_t*)x = 10;
+    return 3;
+  }
+  constexpr uintptr_t gg4 = g4(); // expected-error {{must be initialized by a constant expression}} \
+  // interpreter-note {{in call to 'g4()'}}
+  constexpr int g5() {
+    int &x = x; // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
+    // interpreter-note {{read of uninitialized object is not allowed in a constant expression}}
+    return 3;
+  }
+  constexpr uintptr_t gg5 = g5(); // interpreter-error {{must be initialized by a constant expression}} \
+  // interpreter-note {{in call to 'g5()'}}
----------------
efriedma-quic wrote:

The relevant standard language is, I think, "all id-expressions and uses of *this that refer to an object or reference whose lifetime did not begin with the evaluation of E[...]".  For a local variable inside a constexpr function, I think it does "begin with the evaluation of E", even if its lifetime hasn't yet started, because it's part of the constexpr call frame.  Therefore it's not allowed. I might be missing something, though.

There's also a sort of practical argument: it's weird to say that `int &x = x;` in a constexpr function is well-defined during constant evaluation, but has undefined behavior at runtime.

That said, the compiler's state is internally consistent either way.

https://github.com/llvm/llvm-project/pull/132990


More information about the cfe-commits mailing list