diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2c5308fbcb319..ea1abb71fd4b7 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -153,6 +153,9 @@ C++2c Feature Support - Implemented `P2748R5 Disallow Binding a Returned Glvalue to a Temporary `_. +- Implemented `P2809R3: Trivial infinite loops are not Undefined Behavior `_. + + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Substitute template parameter pack, when it is not explicitly specified @@ -294,6 +297,10 @@ Modified Compiler Flags - Carved out ``-Wformat`` warning about scoped enums into a subwarning and make it controlled by ``-Wformat-pedantic``. Fixes #GH88595. +- Trivial infinite loops (i.e loops with a constant controlling expresion + evaluating to ``true`` and an empty body such as ``while(1);``) + are considered infinite, even when the ``-ffinite-loop`` flag is set. + Removed Compiler Flags ------------------------- diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 864da4e1157f7..70247b337815b 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3971,7 +3971,7 @@ def funroll_loops : Flag<["-"], "funroll-loops">, Group, def fno_unroll_loops : Flag<["-"], "fno-unroll-loops">, Group, HelpText<"Turn off loop unroller">, Visibility<[ClangOption, CC1Option]>; def ffinite_loops: Flag<["-"], "ffinite-loops">, Group, - HelpText<"Assume all loops are finite.">, Visibility<[ClangOption, CC1Option]>; + HelpText<"Assume all non-trivial loops are finite.">, Visibility<[ClangOption, CC1Option]>; def fno_finite_loops: Flag<["-"], "fno-finite-loops">, Group, HelpText<"Do not assume that any loop is finite.">, Visibility<[ClangOption, CC1Option]>; diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 576fe2f7a2d46..479945e3b4cb5 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -908,6 +908,69 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { incrementProfileCounter(&S); } +bool CodeGenFunction::checkIfLoopMustProgress(const Expr *ControllingExpression, + bool HasEmptyBody) { + if (CGM.getCodeGenOpts().getFiniteLoops() == + CodeGenOptions::FiniteLoopsKind::Never) + return false; + + // Now apply rules for plain C (see 6.8.5.6 in C11). + // Loops with constant conditions do not have to make progress in any C + // version. + // As an extension, we consisider loops whose constant expression + // can be constant-folded. + Expr::EvalResult Result; + bool CondIsConstInt = + !ControllingExpression || + (ControllingExpression->EvaluateAsInt(Result, getContext()) && + Result.Val.isInt()); + + bool CondIsTrue = CondIsConstInt && (!ControllingExpression || + Result.Val.getInt().getBoolValue()); + + // Loops with non-constant conditions must make progress in C11 and later. + if (getLangOpts().C11 && !CondIsConstInt) + return true; + + // [C++26][intro.progress] (DR) + // The implementation may assume that any thread will eventually do one of the + // following: + // [...] + // - continue execution of a trivial infinite loop ([stmt.iter.general]). + if (CGM.getCodeGenOpts().getFiniteLoops() == + CodeGenOptions::FiniteLoopsKind::Always || + getLangOpts().CPlusPlus11) { + if (HasEmptyBody && CondIsTrue) { + CurFn->removeFnAttr(llvm::Attribute::MustProgress); + return false; + } + return true; + } + return false; +} + +// [C++26][stmt.iter.general] (DR) +// A trivially empty iteration statement is an iteration statement matching one +// of the following forms: +// - while ( expression ) ; +// - while ( expression ) { } +// - do ; while ( expression ) ; +// - do { } while ( expression ) ; +// - for ( init-statement expression(opt); ) ; +// - for ( init-statement expression(opt); ) { } +template static bool hasEmptyLoopBody(const LoopStmt &S) { + if constexpr (std::is_same_v) { + if (S.getInc()) + return false; + } + const Stmt *Body = S.getBody(); + if (!Body || isa(Body)) + return true; + if (const CompoundStmt *Compound = dyn_cast(Body)) + return Compound->body_empty(); + return false; +} + void CodeGenFunction::EmitWhileStmt(const WhileStmt &S, ArrayRef WhileAttrs) { // Emit the header for the loop, which will also become @@ -942,13 +1005,12 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S, // while(1) is common, avoid extra exit blocks. Be sure // to correctly handle break/continue though. llvm::ConstantInt *C = dyn_cast(BoolCondVal); - bool CondIsConstInt = C != nullptr; - bool EmitBoolCondBranch = !CondIsConstInt || !C->isOne(); + bool EmitBoolCondBranch = !C || !C->isOne(); const SourceRange &R = S.getSourceRange(); LoopStack.push(LoopHeader.getBlock(), CGM.getContext(), CGM.getCodeGenOpts(), WhileAttrs, SourceLocToDebugLoc(R.getBegin()), SourceLocToDebugLoc(R.getEnd()), - checkIfLoopMustProgress(CondIsConstInt)); + checkIfLoopMustProgress(S.getCond(), hasEmptyLoopBody(S))); // When single byte coverage mode is enabled, add a counter to loop condition. if (llvm::EnableSingleByteCoverage) @@ -1059,14 +1121,13 @@ void CodeGenFunction::EmitDoStmt(const DoStmt &S, // "do {} while (0)" is common in macros, avoid extra blocks. Be sure // to correctly handle break/continue though. llvm::ConstantInt *C = dyn_cast(BoolCondVal); - bool CondIsConstInt = C; bool EmitBoolCondBranch = !C || !C->isZero(); const SourceRange &R = S.getSourceRange(); LoopStack.push(LoopBody, CGM.getContext(), CGM.getCodeGenOpts(), DoAttrs, SourceLocToDebugLoc(R.getBegin()), SourceLocToDebugLoc(R.getEnd()), - checkIfLoopMustProgress(CondIsConstInt)); + checkIfLoopMustProgress(S.getCond(), hasEmptyLoopBody(S))); // As long as the condition is true, iterate the loop. if (EmitBoolCondBranch) { @@ -1109,15 +1170,11 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S, llvm::BasicBlock *CondBlock = CondDest.getBlock(); EmitBlock(CondBlock); - Expr::EvalResult Result; - bool CondIsConstInt = - !S.getCond() || S.getCond()->EvaluateAsInt(Result, getContext()); - const SourceRange &R = S.getSourceRange(); LoopStack.push(CondBlock, CGM.getContext(), CGM.getCodeGenOpts(), ForAttrs, SourceLocToDebugLoc(R.getBegin()), SourceLocToDebugLoc(R.getEnd()), - checkIfLoopMustProgress(CondIsConstInt)); + checkIfLoopMustProgress(S.getCond(), hasEmptyLoopBody(S))); // Create a cleanup scope for the condition variable cleanups. LexicalScope ConditionScope(*this, S.getSourceRange()); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 87766a758311d..546beae4af59e 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1471,6 +1471,8 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, // Ensure that the function adheres to the forward progress guarantee, which // is required by certain optimizations. + // In C++11 and up, the attribute will be removed if the body contains a + // trivial empty loop. if (checkIfFunctionMustProgress()) CurFn->addFnAttr(llvm::Attribute::MustProgress); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 6e7417fc7f52b..01ef034e52339 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -636,28 +636,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// Returns true if a loop must make progress, which means the mustprogress /// attribute can be added. \p HasConstantCond indicates whether the branch /// condition is a known constant. - bool checkIfLoopMustProgress(bool HasConstantCond) { - if (CGM.getCodeGenOpts().getFiniteLoops() == - CodeGenOptions::FiniteLoopsKind::Always) - return true; - if (CGM.getCodeGenOpts().getFiniteLoops() == - CodeGenOptions::FiniteLoopsKind::Never) - return false; - - // If the containing function must make progress, loops also must make - // progress (as in C++11 and later). - if (checkIfFunctionMustProgress()) - return true; - - // Now apply rules for plain C (see 6.8.5.6 in C11). - // Loops with constant conditions do not have to make progress in any C - // version. - if (HasConstantCond) - return false; - - // Loops with non-constant conditions must make progress in C11 and later. - return getLangOpts().C11; - } + bool checkIfLoopMustProgress(const Expr *, bool HasEmptyBody); const CodeGen::CGBlockInfo *BlockInfo = nullptr; llvm::Value *BlockPointer = nullptr; diff --git a/clang/test/CodeGen/attr-mustprogress.c b/clang/test/CodeGen/attr-mustprogress.c index b4f8710a9d692..a5e8dc5cd5d5e 100644 --- a/clang/test/CodeGen/attr-mustprogress.c +++ b/clang/test/CodeGen/attr-mustprogress.c @@ -30,7 +30,7 @@ int b = 0; // CHECK: for.cond: // C99-NOT: br {{.*}}!llvm.loop // C11-NOT: br {{.*}}!llvm.loop -// FINITE-NEXT: br {{.*}}!llvm.loop +// FINITE-NOR: br {{.*}}!llvm.loop // void f0(void) { for (; ;) ; @@ -45,7 +45,7 @@ void f0(void) { // CHECK: for.body: // C99-NOT: br {{.*}}, !llvm.loop // C11-NOT: br {{.*}}, !llvm.loop -// FINITE-NEXT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: for.end: // CHECK-NEXT: ret void // @@ -84,7 +84,7 @@ void f2(void) { // CHECK: for.body: // C99-NOT: br {{.*}}, !llvm.loop // C11-NOT: br {{.*}}, !llvm.loop -// FINITE-NEXT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: for.end: // CHECK-NEXT: br label %for.cond1 // CHECK: for.cond1: @@ -113,7 +113,7 @@ void F(void) { // CHECK: while.body: // C99-NOT: br {{.*}}, !llvm.loop // C11-NOT: br {{.*}}, !llvm.loop -// FINITE-NEXT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // void w1(void) { while (1) { @@ -159,7 +159,7 @@ void w2(void) { // CHECK: while.body2: // C99-NOT: br {{.*}} !llvm.loop // C11-NOT: br {{.*}} !llvm.loop -// FINITE-NEXT: br {{.*}} !llvm.loop +// FINITE-NOT: br {{.*}} !llvm.loop // void W(void) { while (a == b) { @@ -177,7 +177,7 @@ void W(void) { // CHECK: do.cond: // C99-NOT: br {{.*}}, !llvm.loop // C11-NOT: br {{.*}}, !llvm.loop -// FINITE-NEXT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: do.end: // CHECK-NEXT: ret void // diff --git a/clang/test/CodeGenCXX/attr-mustprogress.cpp b/clang/test/CodeGenCXX/attr-mustprogress.cpp index 843f5460426cc..0cb18df25223e 100644 --- a/clang/test/CodeGenCXX/attr-mustprogress.cpp +++ b/clang/test/CodeGenCXX/attr-mustprogress.cpp @@ -24,21 +24,21 @@ int b = 0; // CHECK: datalayout // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z2f0v( // CHECK-NEXT: entry: // CHECK-NEXT: br label %for.cond // CHECK: for.cond: // CXX98-NOT: br {{.*}} llvm.loop -// CXX11-NEXT: br label %for.cond, !llvm.loop [[LOOP1:!.*]] -// FINITE-NEXT: br label %for.cond, !llvm.loop [[LOOP1:!.*]] +// CXX11-NOT: br {{.*}} llvm.loop +// FINITE-NOT: br {{.*}} llvm.loop void f0() { for (; ;) ; } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z2f1v( // CHECK-NEXT: entry: @@ -46,9 +46,9 @@ void f0() { // CHECK: for.cond: // CHECK-NEXT: br i1 true, label %for.body, label %for.end // CHECK: for.body: -// CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br label %for.cond, !llvm.loop [[LOOP2:!.*]] -// FINITE-NEXT: br label %for.cond, !llvm.loop [[LOOP2:!.*]] +// CXX98-NOT: br {{.*}}, !llvm.loop +// CXX11-NOT: br {{.*}} llvm.loop +// FINITE-NOT: br {{.*}} llvm.loop // CHECK: for.end: // CHECK-NEXT: ret void // @@ -81,7 +81,7 @@ void f2() { } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z1Fv( // CHECK-NEXT: entry: @@ -90,8 +90,8 @@ void f2() { // CHECK-NEXT: br i1 true, label %for.body, label %for.end // CHECK: for.body: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br label %for.cond, !llvm.loop [[LOOP4:!.*]] -// FINITE-NEXT: br label %for.cond, !llvm.loop [[LOOP4:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: for.end: // CHECK-NEXT: br label %for.cond1 // CHECK: for.cond1: @@ -114,7 +114,7 @@ void F() { } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z2F2v( // CHECK-NEXT: entry: @@ -134,8 +134,8 @@ void F() { // CHECK-NEXT: br i1 true, label %for.body2, label %for.end3 // CHECK: for.body2: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br label %for.cond1, !llvm.loop [[LOOP7:!.*]] -// FINITE-NEXT: br label %for.cond1, !llvm.loop [[LOOP7:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: for.end3: // CHECK-NEXT: ret void // @@ -147,15 +147,15 @@ void F2() { } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z2w1v( // CHECK-NEXT: entry: // CHECK-NEXT: br label %while.body // CHECK: while.body: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br label %while.body, !llvm.loop [[LOOP8:!.*]] -// FINITE-NEXT: br label %while.body, !llvm.loop [[LOOP8:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // void w1() { while (1) @@ -186,7 +186,7 @@ void w2() { } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z1Wv( // CHECK-NEXT: entry: @@ -204,8 +204,8 @@ void w2() { // CHECK-NEXT: br label %while.body2 // CHECK: while.body2: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br label %while.body2, !llvm.loop [[LOOP11:!.*]] -// FINITE-NEXT: br label %while.body2, !llvm.loop [[LOOP11:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // void W() { while (a == b) @@ -215,15 +215,15 @@ void W() { } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z2W2v( // CHECK-NEXT: entry: // CHECK-NEXT: br label %while.body // CHECK: while.body: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br label %while.body, !llvm.loop [[LOOP12:!.*]] -// FINITE-NEXT: br label %while.body, !llvm.loop [[LOOP12:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // void W2() { while (1) @@ -233,7 +233,7 @@ void W2() { } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z2d1v( // CHECK-NEXT: entry: @@ -242,8 +242,8 @@ void W2() { // CHECK-NEXT: br label %do.cond // CHECK: do.cond: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br i1 true, label %do.body, label %do.end, !llvm.loop [[LOOP13:!.*]] -// FINITE-NEXT: br i1 true, label %do.body, label %do.end, !llvm.loop [[LOOP13:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: do.end: // CHECK-NEXT: ret void // @@ -278,7 +278,7 @@ void d2() { } // CXX98-NOT: mustprogress -// CXX11: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z1Dv( // CHECK-NEXT: entry: @@ -287,8 +287,8 @@ void d2() { // CHECK-NEXT: br label %do.cond // CHECK: do.cond: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br i1 true, label %do.body, label %do.end, !llvm.loop [[LOOP15:!.*]] -// FINITE-NEXT: br i1 true, label %do.body, label %do.end, !llvm.loop [[LOOP15:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: do.end: // CHECK-NEXT: br label %do.body1 // CHECK: do.body1: @@ -312,8 +312,8 @@ void D() { while (a == b); } -// CXX98-NOT : mustprogress -// CXX11: mustprogress +// CXX98-NOT: mustprogress +// CXX11-NOT: mustprogress // FINITE-NOT: mustprogress // CHECK-LABEL: @_Z2D2v( // CHECK-NEXT: entry: @@ -333,8 +333,8 @@ void D() { // CHECK-NEXT: br label %do.cond2 // CHECK: do.cond2: // CXX98-NOT: br {{.*}}, !llvm.loop -// CXX11-NEXT: br i1 true, label %do.body1, label %do.end3, !llvm.loop [[LOOP18:!.*]] -// FINITE-NEXT: br i1 true, label %do.body1, label %do.end3, !llvm.loop [[LOOP18:!.*]] +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop // CHECK: do.end3: // CHECK-NEXT: ret void // @@ -347,22 +347,75 @@ void D2() { while (1); } -// CXX11: [[LOOP1]] = distinct !{[[LOOP1]], [[MP:!.*]]} +// CXX98-NOT: mustprogress +// CXX11-NOT: mustprogress +// FINITE-NOT: mustprogress +// CHECK-LABEL: @_Z9compound0v( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label %for.cond +// CHECK: for.cond: +// CXX98-NOT: br {{.*}}, !llvm.loop +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop +void compound0() { + for (; ;) {} +} + +// CXX98-NOT: mustprogress +// CXX11-NOT: mustprogress +// FINITE-NOT: mustprogress +// CHECK-LABEL: @_Z9compound1v( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label %for.cond +// CHECK: for.cond: +// CXX98-NOT: br {{.*}}, llvm.loop +// CXX11-NOT: br {{.*}}, llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop +void compound1() { + for (; ;) {/*! */} +} + +// CXX98-NOT: mustprogress +// CXX11-NOT: mustprogress +// FINITE-NOT: mustprogress +// CHECK-LABEL: @_Z9compound2v( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label %do.body +// CHECK: do.body: +// CHECK-NEXT: br label %do.cond +// CHECK: do.cond: +// CXX98-NOT: br {{.*}}, !llvm.loop +// CXX11-NOT: br {{.*}}, !llvm.loop +// FINITE-NOT: br {{.*}}, !llvm.loop +// CHECK: do.end: +// CHECK-NEXT: ret void +// +void compound2() { + do {} while (1+1); +} + +// CXX98-NOT: mustprogress +// CXX11 : mustprogress +// FINITE : mustprogress +// CHECK-LABEL: @_Z5Falsev( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label %do.body +// CHECK: do.body: +// CHECK-NEXT: br label %do.end +// CHECK: do.end: +// CHECK-NEXT: ret void +// +void False() { + do {} while (1-1); +} + + +// CXX11: [[LOOP3]] = distinct !{[[LOOP3]], [[MP:.*]]} // CXX11: [[MP]] = !{!"llvm.loop.mustprogress"} -// CXX11: [[LOOP2]] = distinct !{[[LOOP2]], [[MP]]} -// CXX11: [[LOOP3]] = distinct !{[[LOOP3]], [[MP]]} -// CXX11: [[LOOP4]] = distinct !{[[LOOP4]], [[MP]]} // CXX11: [[LOOP5]] = distinct !{[[LOOP5]], [[MP]]} // CXX11: [[LOOP6]] = distinct !{[[LOOP6]], [[MP]]} -// CXX11: [[LOOP7]] = distinct !{[[LOOP7]], [[MP]]} -// CXX11: [[LOOP8]] = distinct !{[[LOOP8]], [[MP]]} // CXX11: [[LOOP9]] = distinct !{[[LOOP9]], [[MP]]} // CXX11: [[LOOP10]] = distinct !{[[LOOP10]], [[MP]]} -// CXX11: [[LOOP11]] = distinct !{[[LOOP11]], [[MP]]} -// CXX11: [[LOOP12]] = distinct !{[[LOOP12]], [[MP]]} -// CXX11: [[LOOP13]] = distinct !{[[LOOP13]], [[MP]]} // CXX11: [[LOOP14]] = distinct !{[[LOOP14]], [[MP]]} -// CXX11: [[LOOP15]] = distinct !{[[LOOP15]], [[MP]]} // CXX11: [[LOOP16]] = distinct !{[[LOOP16]], [[MP]]} // CXX11: [[LOOP17]] = distinct !{[[LOOP17]], [[MP]]} -// CXX11: [[LOOP18]] = distinct !{[[LOOP18]], [[MP]]} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 260f74ded93c5..7b7b2688f3f48 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -187,7 +187,7 @@

C++2c implementation status

Trivial infinite loops are not Undefined Behavior P2809R3 (DR) - No + Clang 19 Erroneous behaviour for uninitialized reads @@ -466,7 +466,7 @@

C++23 implementation status

Clang 19 (Partial) The lifetime extension of temporaries bound to member references - by default member initializers in aggregate initialization was + by default member initializers in aggregate initialization was not supported now.