https://github.com/filaka771 updated 
https://github.com/llvm/llvm-project/pull/226775

>From 15c92c19426f06b3b1a7a85015637380a0ca0ed5 Mon Sep 17 00:00:00 2001
From: Alex Filak <[email protected]>
Date: Fri, 18 Sep 2026 22:56:35 +0300
Subject: [PATCH 1/3] [clang][Sema] Avoid -Wconstant-conversion in unselected
 conditional operands

---
 clang/include/clang/Sema/Sema.h |   5 +-
 clang/lib/Sema/SemaChecking.cpp | 186 +++++++++++++++++++++-----------
 2 files changed, 128 insertions(+), 63 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index eb3b908351448d..7444661182266c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2923,9 +2923,12 @@ class Sema final : public SemaBase {
   /// since all 1s are not contiguous.
   bool ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum);
 
+  /// Check the implicit conversion of E to T. IsKnownUnreachable indicates 
that
+  /// E is within an unselected operand of a constant conditional expression.
   void CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
                                bool *ICContext = nullptr,
-                               bool IsListInit = false);
+                               bool IsListInit = false,
+                               bool IsKnownUnreachable = false);
 
   /// Check for overflow behavior type related implicit conversion diagnostics.
   /// Returns true if OBT-related diagnostic was issued, false otherwise.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 687af75c1496be..140b0be3b4448a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12365,7 +12365,8 @@ static bool IsSameFloatAfterCast(const APValue &value,
 }
 
 static void AnalyzeImplicitConversions(Sema &S, Expr *E, SourceLocation CC,
-                                       bool IsListInit = false);
+                                       bool IsListInit = false,
+                                       bool IsKnownUnreachable = false);
 
 static bool IsEnumConstOrFromMacro(Sema &S, const Expr *E) {
   // Suppress cases where we are comparing against an enum constant.
@@ -12700,26 +12701,30 @@ static bool CheckTautologicalComparison(Sema &S, 
BinaryOperator *E,
 
 /// Analyze the operands of the given comparison.  Implements the
 /// fallback case from AnalyzeComparison.
-static void AnalyzeImpConvsInComparison(Sema &S, BinaryOperator *E) {
-  AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
-  AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
+static void AnalyzeImpConvsInComparison(Sema &S, BinaryOperator *E,
+                                        bool IsKnownUnreachable) {
+  AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc(),
+                             /*IsListInit=*/false, IsKnownUnreachable);
+  AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc(),
+                             /*IsListInit=*/false, IsKnownUnreachable);
 }
 
 /// Implements -Wsign-compare.
 ///
 /// \param E the binary operator to check for warnings
-static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
+static void AnalyzeComparison(Sema &S, BinaryOperator *E,
+                              bool IsKnownUnreachable) {
   // The type the comparison is being performed in.
   QualType T = E->getLHS()->getType();
 
   // Only analyze comparison operators where both sides have been converted to
   // the same type.
   if (!S.Context.hasSameUnqualifiedType(T, E->getRHS()->getType()))
-    return AnalyzeImpConvsInComparison(S, E);
+    return AnalyzeImpConvsInComparison(S, E, IsKnownUnreachable);
 
   // Don't analyze value-dependent comparisons directly.
   if (E->isValueDependent())
-    return AnalyzeImpConvsInComparison(S, E);
+    return AnalyzeImpConvsInComparison(S, E, IsKnownUnreachable);
 
   Expr *LHS = E->getLHS();
   Expr *RHS = E->getRHS();
@@ -12732,7 +12737,7 @@ static void AnalyzeComparison(Sema &S, BinaryOperator 
*E) {
 
     // We don't care about expressions whose result is a constant.
     if (RHSValue && LHSValue)
-      return AnalyzeImpConvsInComparison(S, E);
+      return AnalyzeImpConvsInComparison(S, E, IsKnownUnreachable);
 
     // We only care about expressions where just one side is literal
     if ((bool)RHSValue ^ (bool)LHSValue) {
@@ -12745,7 +12750,7 @@ static void AnalyzeComparison(Sema &S, BinaryOperator 
*E) {
       // Check whether an integer constant comparison results in a value
       // of 'true' or 'false'.
       if (CheckTautologicalComparison(S, E, Const, Other, Value, RhsConstant))
-        return AnalyzeImpConvsInComparison(S, E);
+        return AnalyzeImpConvsInComparison(S, E, IsKnownUnreachable);
     }
   }
 
@@ -12753,7 +12758,7 @@ static void AnalyzeComparison(Sema &S, BinaryOperator 
*E) {
     // We don't do anything special if this isn't an unsigned integral
     // comparison:  we're only interested in integral comparisons, and
     // signed comparisons only happen in cases we don't care to warn about.
-    return AnalyzeImpConvsInComparison(S, E);
+    return AnalyzeImpConvsInComparison(S, E, IsKnownUnreachable);
   }
 
   LHS = LHS->IgnoreParenImpCasts();
@@ -12781,7 +12786,7 @@ static void AnalyzeComparison(Sema &S, BinaryOperator 
*E) {
     signedOperand = RHS;
     unsignedOperand = LHS;
   } else {
-    return AnalyzeImpConvsInComparison(S, E);
+    return AnalyzeImpConvsInComparison(S, E, IsKnownUnreachable);
   }
 
   // Otherwise, calculate the effective range of the signed operand.
@@ -12793,8 +12798,10 @@ static void AnalyzeComparison(Sema &S, BinaryOperator 
*E) {
 
   // Go ahead and analyze implicit conversions in the operands.  Note
   // that we skip the implicit conversions on both sides.
-  AnalyzeImplicitConversions(S, LHS, E->getOperatorLoc());
-  AnalyzeImplicitConversions(S, RHS, E->getOperatorLoc());
+  AnalyzeImplicitConversions(S, LHS, E->getOperatorLoc(), /*IsListInit=*/false,
+                             IsKnownUnreachable);
+  AnalyzeImplicitConversions(S, RHS, E->getOperatorLoc(), /*IsListInit=*/false,
+                             IsKnownUnreachable);
 
   // If the signed range is non-negative, -Wsign-compare won't fire.
   if (signedRange->NonNegative)
@@ -12988,9 +12995,11 @@ static bool AnalyzeBitFieldAssignment(Sema &S, 
FieldDecl *Bitfield, Expr *Init,
 
 /// Analyze the given simple or compound assignment for warning-worthy
 /// operations.
-static void AnalyzeAssignment(Sema &S, BinaryOperator *E) {
+static void AnalyzeAssignment(Sema &S, BinaryOperator *E,
+                              bool IsKnownUnreachable) {
   // Just recurse on the LHS.
-  AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
+  AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc(),
+                             /*IsListInit=*/false, IsKnownUnreachable);
 
   // We want to recurse on the RHS as normal unless we're assigning to
   // a bitfield.
@@ -12998,8 +13007,9 @@ static void AnalyzeAssignment(Sema &S, BinaryOperator 
*E) {
     if (AnalyzeBitFieldAssignment(S, Bitfield, E->getRHS(),
                                   E->getOperatorLoc())) {
       // Recurse, ignoring any implicit conversions on the RHS.
-      return AnalyzeImplicitConversions(S, E->getRHS()->IgnoreParenImpCasts(),
-                                        E->getOperatorLoc());
+      return AnalyzeImplicitConversions(
+          S, E->getRHS()->IgnoreParenImpCasts(), E->getOperatorLoc(),
+          /*IsListInit=*/false, IsKnownUnreachable);
     }
   }
 
@@ -13008,7 +13018,8 @@ static void AnalyzeAssignment(Sema &S, BinaryOperator 
*E) {
   llvm::SaveAndRestore OBTAssignmentContext(
       S.InOverflowBehaviorAssignmentContext, true);
 
-  AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
+  AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc(),
+                             /*IsListInit=*/false, IsKnownUnreachable);
 
   // Diagnose implicitly sequentially-consistent atomic assignment.
   if (E->getLHS()->getType()->isAtomicType())
@@ -13160,12 +13171,15 @@ static void DiagnoseFloatingImpCast(Sema &S, const 
Expr *E, QualType T,
 
 /// Analyze the given compound assignment for the possible losing of
 /// floating-point precision.
-static void AnalyzeCompoundAssignment(Sema &S, BinaryOperator *E) {
+static void AnalyzeCompoundAssignment(Sema &S, BinaryOperator *E,
+                                      bool IsKnownUnreachable) {
   assert(isa<CompoundAssignOperator>(E) &&
          "Must be compound assignment operation");
   // Recurse on the LHS and RHS in here
-  AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
-  AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
+  AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc(),
+                             /*IsListInit=*/false, IsKnownUnreachable);
+  AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc(),
+                             /*IsListInit=*/false, IsKnownUnreachable);
 
   if (E->getLHS()->getType()->isAtomicType())
     S.Diag(E->getOperatorLoc(), diag::warn_atomic_implicit_seq_cst);
@@ -13442,7 +13456,8 @@ bool Sema::DiscardingCFIUncheckedCallee(QualType From, 
QualType To) const {
 }
 
 void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
-                                   bool *ICContext, bool IsListInit) {
+                                   bool *ICContext, bool IsListInit,
+                                   bool IsKnownUnreachable) {
   if (E->isTypeDependent() || E->isValueDependent()) return;
 
   const Type *Source = Context.getCanonicalType(E->getType()).getTypePtr();
@@ -13879,11 +13894,12 @@ void Sema::CheckImplicitConversion(Expr *E, QualType 
T, SourceLocation CC,
       std::string PrettySourceValue = toString(Value, 10);
       std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
 
-      DiagRuntimeBehavior(E->getExprLoc(), E,
-                          PDiag(diag::warn_impcast_integer_precision_constant)
-                              << PrettySourceValue << PrettyTargetValue
-                              << E->getType() << T << E->getSourceRange()
-                              << SourceRange(CC));
+      if (!IsKnownUnreachable)
+        DiagRuntimeBehavior(E->getExprLoc(), E,
+                            
PDiag(diag::warn_impcast_integer_precision_constant)
+                                << PrettySourceValue << PrettyTargetValue
+                                << E->getType() << T << E->getSourceRange()
+                                << SourceRange(CC));
       return;
     }
 
@@ -13932,10 +13948,12 @@ void Sema::CheckImplicitConversion(Expr *E, QualType 
T, SourceLocation CC,
         std::string PrettySourceValue = toString(Value, 10);
         std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
 
-        Diag(E->getExprLoc(),
-             PDiag(diag::warn_impcast_integer_precision_constant)
-                 << PrettySourceValue << PrettyTargetValue << E->getType() << T
-                 << E->getSourceRange() << SourceRange(CC));
+        if (!IsKnownUnreachable && !isUnevaluatedContext() &&
+            !currentEvaluationContext().isDiscardedStatementContext())
+          Diag(E->getExprLoc(),
+               PDiag(diag::warn_impcast_integer_precision_constant)
+                   << PrettySourceValue << PrettyTargetValue << E->getType()
+                   << T << E->getSourceRange() << SourceRange(CC));
         return;
       }
     }
@@ -13997,34 +14015,65 @@ void Sema::CheckImplicitConversion(Expr *E, QualType 
T, SourceLocation CC,
 }
 
 static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
-                                     SourceLocation CC, QualType T);
+                                     SourceLocation CC, QualType T,
+                                     bool IsKnownUnreachable);
 
 static void CheckConditionalOperand(Sema &S, Expr *E, QualType T,
-                                    SourceLocation CC, bool &ICContext) {
+                                    SourceLocation CC, bool &ICContext,
+                                    bool IsKnownUnreachable) {
   E = E->IgnoreParenImpCasts();
   // Diagnose incomplete type for second or third operand in C.
   if (!S.getLangOpts().CPlusPlus && E->getType()->isRecordType())
     S.RequireCompleteExprType(E, diag::err_incomplete_type);
 
   if (auto *CO = dyn_cast<AbstractConditionalOperator>(E))
-    return CheckConditionalOperator(S, CO, CC, T);
+    return CheckConditionalOperator(S, CO, CC, T, IsKnownUnreachable);
 
-  AnalyzeImplicitConversions(S, E, CC);
+  AnalyzeImplicitConversions(S, E, CC, /*IsListInit=*/false,
+                             IsKnownUnreachable);
   if (E->getType() != T)
-    return S.CheckImplicitConversion(E, T, CC, &ICContext);
+    return S.CheckImplicitConversion(E, T, CC, &ICContext,
+                                     /*IsListInit=*/false, IsKnownUnreachable);
 }
 
 static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
-                                     SourceLocation CC, QualType T) {
-  AnalyzeImplicitConversions(S, E->getCond(), E->getQuestionLoc());
+                                     SourceLocation CC, QualType T,
+                                     bool IsKnownUnreachable) {
+  AnalyzeImplicitConversions(S, E->getCond(), E->getQuestionLoc(),
+                             /*IsListInit=*/false, IsKnownUnreachable);
+
+  bool CondValue;
+  bool CondIsKnown = E->getCond()->EvaluateAsBooleanCondition(
+      CondValue, S.Context, S.isConstantEvaluatedContext());
+  if (CondIsKnown && !S.isConstantEvaluatedContext()) {
+    // The condition may also be evaluated at compile time, for example in a
+    // constexpr function. Only prune an operand if both evaluations agree;
+    // __builtin_is_constant_evaluated() can produce different results.
+    bool ConstantCondValue;
+    CondIsKnown =
+        E->getCond()->EvaluateAsBooleanCondition(ConstantCondValue, S.Context,
+                                                 /*InConstantContext=*/true) &&
+        ConstantCondValue == CondValue;
+  }
 
   Expr *TrueExpr = E->getTrueExpr();
-  if (auto *BCO = dyn_cast<BinaryConditionalOperator>(E))
+  bool TrueIsKnownUnreachable =
+      IsKnownUnreachable || (CondIsKnown && !CondValue);
+  bool FalseIsKnownUnreachable =
+      IsKnownUnreachable || (CondIsKnown && CondValue);
+
+  if (auto *BCO = dyn_cast<BinaryConditionalOperator>(E)) {
+    // The common expression supplies the condition as well as the true value,
+    // so its subexpressions are evaluated even when the condition is false.
     TrueExpr = BCO->getCommon();
+    TrueIsKnownUnreachable = IsKnownUnreachable;
+  }
 
   bool Suspicious = false;
-  CheckConditionalOperand(S, TrueExpr, T, CC, Suspicious);
-  CheckConditionalOperand(S, E->getFalseExpr(), T, CC, Suspicious);
+  CheckConditionalOperand(S, TrueExpr, T, CC, Suspicious,
+                          TrueIsKnownUnreachable);
+  CheckConditionalOperand(S, E->getFalseExpr(), T, CC, Suspicious,
+                          FalseIsKnownUnreachable);
 
   if (T->isBooleanType())
     DiagnoseIntInBoolContext(S, E);
@@ -14043,10 +14092,12 @@ static void CheckConditionalOperator(Sema &S, 
AbstractConditionalOperator *E,
 
   Suspicious = false;
   S.CheckImplicitConversion(TrueExpr->IgnoreParenImpCasts(), E->getType(), CC,
-                            &Suspicious);
+                            &Suspicious, /*IsListInit=*/false,
+                            TrueIsKnownUnreachable);
   if (!Suspicious)
     S.CheckImplicitConversion(E->getFalseExpr()->IgnoreParenImpCasts(),
-                              E->getType(), CC, &Suspicious);
+                              E->getType(), CC, &Suspicious,
+                              /*IsListInit=*/false, FalseIsKnownUnreachable);
 }
 
 /// Check conversion of given expression to boolean.
@@ -14067,18 +14118,22 @@ struct AnalyzeImplicitConversionsWorkItem {
   Expr *E;
   SourceLocation CC;
   bool IsListInit;
+  // Track unselected conditional operands without skipping other diagnostics
+  // that do not depend on whether the expression is evaluated.
+  bool IsKnownUnreachable;
 };
 }
 
 static void CheckCommaOperand(
     Sema &S, Expr *E, QualType T, SourceLocation CC,
-    bool ExtraCheckForImplicitConversion,
+    bool ExtraCheckForImplicitConversion, bool IsKnownUnreachable,
     llvm::SmallVectorImpl<AnalyzeImplicitConversionsWorkItem> &WorkList) {
   E = E->IgnoreParenImpCasts();
-  WorkList.push_back({E, CC, false});
+  WorkList.push_back({E, CC, false, IsKnownUnreachable});
 
   if (ExtraCheckForImplicitConversion && E->getType() != T)
-    S.CheckImplicitConversion(E, T, CC);
+    S.CheckImplicitConversion(E, T, CC, /*ICContext=*/nullptr,
+                              /*IsListInit=*/false, IsKnownUnreachable);
 }
 
 /// Data recursive variant of AnalyzeImplicitConversions. Subexpressions
@@ -14156,9 +14211,11 @@ static void AnalyzeImplicitConversions(
       /// how CheckConditionalOperand behaves; it's as-if the correct operand
       /// were directly used for the implicit conversion check.
       CheckCommaOperand(S, BO->getLHS(), T, BO->getOperatorLoc(),
-                        /*ExtraCheckForImplicitConversion=*/false, WorkList);
+                        /*ExtraCheckForImplicitConversion=*/false,
+                        Item.IsKnownUnreachable, WorkList);
       CheckCommaOperand(S, BO->getRHS(), T, BO->getOperatorLoc(),
-                        /*ExtraCheckForImplicitConversion=*/true, WorkList);
+                        /*ExtraCheckForImplicitConversion=*/true,
+                        Item.IsKnownUnreachable, WorkList);
       return;
     }
   }
@@ -14166,7 +14223,7 @@ static void AnalyzeImplicitConversions(
   // For conditional operators, we analyze the arguments as if they
   // were being fed directly into the output.
   if (auto *CO = dyn_cast<AbstractConditionalOperator>(SourceExpr)) {
-    CheckConditionalOperator(S, CO, CC, T);
+    CheckConditionalOperator(S, CO, CC, T, Item.IsKnownUnreachable);
     return;
   }
 
@@ -14178,7 +14235,8 @@ static void AnalyzeImplicitConversions(
   // The non-canonical typecheck is just an optimization;
   // CheckImplicitConversion will filter out dead implicit conversions.
   if (SourceExpr->getType() != T)
-    S.CheckImplicitConversion(SourceExpr, T, CC, nullptr, IsListInit);
+    S.CheckImplicitConversion(SourceExpr, T, CC, nullptr, IsListInit,
+                              Item.IsKnownUnreachable);
 
   // Now continue drilling into this expression.
 
@@ -14188,7 +14246,8 @@ static void AnalyzeImplicitConversions(
     // FIXME: Use a more uniform representation for this.
     for (auto *SE : POE->semantics())
       if (auto *OVE = dyn_cast<OpaqueValueExpr>(SE))
-        WorkList.push_back({OVE->getSourceExpr(), CC, IsListInit});
+        WorkList.push_back(
+            {OVE->getSourceExpr(), CC, IsListInit, Item.IsKnownUnreachable});
   }
 
   // Skip past explicit casts.
@@ -14208,33 +14267,35 @@ static void AnalyzeImplicitConversions(
     E = E->IgnoreParenImpCasts();
     if (!CE->getType()->isVoidType() && E->getType()->isAtomicType())
       S.Diag(E->getBeginLoc(), diag::warn_atomic_implicit_seq_cst);
-    WorkList.push_back({E, CC, IsListInit});
+    WorkList.push_back({E, CC, IsListInit, Item.IsKnownUnreachable});
     return;
   }
 
   if (auto *OutArgE = dyn_cast<HLSLOutArgExpr>(E)) {
-    WorkList.push_back({OutArgE->getArgLValue(), CC, IsListInit});
+    WorkList.push_back(
+        {OutArgE->getArgLValue(), CC, IsListInit, Item.IsKnownUnreachable});
     // The base expression is only used to initialize the parameter for
     // arguments to `inout` parameters, so we only traverse down the base
     // expression for `inout` cases.
     if (OutArgE->isInOut())
-      WorkList.push_back(
-          {OutArgE->getCastedTemporary()->getSourceExpr(), CC, IsListInit});
-    WorkList.push_back({OutArgE->getWritebackCast(), CC, IsListInit});
+      WorkList.push_back({OutArgE->getCastedTemporary()->getSourceExpr(), CC,
+                          IsListInit, Item.IsKnownUnreachable});
+    WorkList.push_back(
+        {OutArgE->getWritebackCast(), CC, IsListInit, 
Item.IsKnownUnreachable});
     return;
   }
 
   if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
     // Do a somewhat different check with comparison operators.
     if (BO->isComparisonOp())
-      return AnalyzeComparison(S, BO);
+      return AnalyzeComparison(S, BO, Item.IsKnownUnreachable);
 
     // And with simple assignments.
     if (BO->getOpcode() == BO_Assign)
-      return AnalyzeAssignment(S, BO);
+      return AnalyzeAssignment(S, BO, Item.IsKnownUnreachable);
     // And with compound assignments.
     if (BO->isAssignmentOp())
-      return AnalyzeCompoundAssignment(S, BO);
+      return AnalyzeCompoundAssignment(S, BO, Item.IsKnownUnreachable);
   }
 
   // These break the otherwise-useful invariant below.  Fortunately,
@@ -14267,7 +14328,7 @@ static void AnalyzeImplicitConversions(
       // Ignore checking string literals that are in logical and operators.
       // This is a common pattern for asserts.
       continue;
-    WorkList.push_back({ChildExpr, CC, IsListInit});
+    WorkList.push_back({ChildExpr, CC, IsListInit, Item.IsKnownUnreachable});
   }
 
   if (BO && BO->isLogicalOp()) {
@@ -14295,9 +14356,10 @@ static void AnalyzeImplicitConversions(
 /// implicit conversions in the given expression.  There are a couple
 /// of competing diagnostics here, -Wconversion and -Wsign-compare.
 static void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC,
-                                       bool IsListInit/*= false*/) {
+                                       bool IsListInit /*= false*/,
+                                       bool IsKnownUnreachable /*= false*/) {
   llvm::SmallVector<AnalyzeImplicitConversionsWorkItem, 16> WorkList;
-  WorkList.push_back({OrigE, CC, IsListInit});
+  WorkList.push_back({OrigE, CC, IsListInit, IsKnownUnreachable});
   while (!WorkList.empty())
     AnalyzeImplicitConversions(S, WorkList.pop_back_val(), WorkList);
 }

>From a196ab3ef9518b80af0aa21f8d9304bfc076ff9b Mon Sep 17 00:00:00 2001
From: Alex Filak <[email protected]>
Date: Fri, 18 Sep 2026 22:57:58 +0300
Subject: [PATCH 2/3] [clang][Sema] Add tests for unselected constant
 conversions

---
 clang/test/Sema/constant-conversion.c      | 115 ++++++++++++
 clang/test/SemaCXX/constant-conversion.cpp | 200 ++++++++++++++++++++-
 2 files changed, 314 insertions(+), 1 deletion(-)

diff --git a/clang/test/Sema/constant-conversion.c 
b/clang/test/Sema/constant-conversion.c
index adba3315cc9b41..45aa15f574830d 100644
--- a/clang/test/Sema/constant-conversion.c
+++ b/clang/test/Sema/constant-conversion.c
@@ -69,6 +69,121 @@ void test5(void) {
   a.b = 100;
 }
 
+// GH223923: Do not diagnose conversions in unselected operands of constant
+// conditional expressions.
+// Exercise both selected and unselected operands in array initializers.
+static const signed char conditional_array[] __attribute__((unused)) = {
+  0 ? 128 : 1,
+  1 ? 1 : 128,
+  1 ? 128 : 1, // expected-warning {{implicit conversion from 'int' to 'signed 
char' changes value from 128 to -128}}
+  0 ? 1 : 128, // expected-warning {{implicit conversion from 'int' to 'signed 
char' changes value from 128 to -128}}
+  // An enclosing unselected operand also suppresses nested conversions.
+  0 ? (1 ? 128 : 1) : 1,
+  1 ? 1 : (0 ? 1 : 128),
+};
+
+void GH223923(int condition) {
+  signed char signed_char_dead = 0 ? 128 : -1;
+  signed char signed_char_live = 1 ? 128 : -1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char signed_char_maybe = condition ? 128 : -1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_operand_dead = 1 ? -1 : 128;
+  signed char false_operand_live = 0 ? -1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_operand_maybe = condition ? -1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+  short short_dead = 0 ? 32768 : 1;
+  short short_live = 1 ? 32768 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'short' changes 
value from 32768 to -32768}}
+
+  int int_dead = 0 ? 2147483648LL : 1;
+  int int_live = 1 ? 2147483648LL : 1;
+  // expected-warning@-1 {{implicit conversion from 'long long' to 'int' 
changes value from 2147483648 to -2147483648}}
+
+  signed char truncation_dead = 0 ? 256 : 1;
+  signed char truncation_live = 1 ? 256 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 256 to 0}}
+
+  signed char sink;
+  int assignment_dead = 0 ? (sink = 128) : 1;
+  int assignment_live = 1 ? (sink = 128) : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+  signed char binary_dead = 1 ?: 128;
+  signed char binary_live = 0 ?: 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+#define CONDITION_FALSE 0
+#define CONDITION_TRUE 1
+#define IS_ZERO(x) ((x) == 0)
+
+void GH223923_macro_conditions(int condition) {
+  signed char object_true_dead = CONDITION_FALSE ? 128 : 1;
+  signed char object_false_dead = CONDITION_TRUE ? 1 : 128;
+  signed char object_true_live = CONDITION_TRUE ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char object_false_live = CONDITION_FALSE ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+  signed char function_true_dead = IS_ZERO(1) ? 128 : 1;
+  signed char function_false_dead = IS_ZERO(2 - 2) ? 1 : 128;
+  signed char function_true_live = IS_ZERO(0) ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char function_false_live = IS_ZERO(1 + 1) ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+  // A macro argument need not be constant: either operand can be selected.
+  signed char function_true_maybe = IS_ZERO(condition) ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char function_false_maybe = IS_ZERO(condition) ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+#undef IS_ZERO
+#undef CONDITION_TRUE
+#undef CONDITION_FALSE
+
+void GH223923_static_const_conditions(void) {
+  // These conditions can be folded even though the variables are not integer
+  // constant expressions in C.
+  static const int zero = 0;
+  static const int one = 1;
+  signed char true_dead = zero ? 128 : 1;
+  signed char false_dead = one ? 1 : 128;
+  signed char true_live = one ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_live = zero ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+void GH223923_generic_conditions(int i, double d) {
+  // Selection depends on the type, not the runtime value of the argument.
+  signed char true_dead = _Generic(d, int: 1, default: 0) ? 128 : 1;
+  signed char false_dead = _Generic(i, int: 1, default: 0) ? 1 : 128;
+  signed char true_live = _Generic(i, int: 1, default: 0) ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_live = _Generic(d, int: 1, default: 0) ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+void GH223923_algebraic_conditions(int x) {
+  // These conditions have fixed results for int, but conversion analysis
+  // remains conservative: neither operand is marked unreachable here.
+  signed char multiply_true = x * 0 ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char multiply_false = x * 0 ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char compare_true = x == x ? 128 : 1;
+  // expected-warning@-1 {{self-comparison always evaluates to true}}
+  // expected-warning@-2 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char compare_false = x == x ? 1 : 128;
+  // expected-warning@-1 {{self-comparison always evaluates to true}}
+  // expected-warning@-2 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
 void test6(void) {
   // Test that unreachable code doesn't trigger the truncation warning.
   unsigned char x = 0 ? 65535 : 1; // no-warning
diff --git a/clang/test/SemaCXX/constant-conversion.cpp 
b/clang/test/SemaCXX/constant-conversion.cpp
index 9be8b139e79e22..fa9de748cff135 100644
--- a/clang/test/SemaCXX/constant-conversion.cpp
+++ b/clang/test/SemaCXX/constant-conversion.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -Wbool-operation -verify -triple 
x86_64-apple-darwin %s
+// RUN: %clang_cc1 -fsyntax-only -Wbool-operation -verify -triple 
x86_64-apple-darwin -fexperimental-new-constant-interpreter %s
 
 // This file tests -Wconstant-conversion, a subcategory of -Wconversion
 // which is on by default.
@@ -23,6 +24,203 @@ void too_big_for_char(int param) {
   char ok4 = true ? 0 : nines() + 1;
 }
 
+namespace GH223923 {
+
+void static_const_conditions() {
+  static const int zero = 0;
+  static const int one = 1;
+  signed char true_dead = zero ? 128 : 1;
+  signed char false_dead = one ? 1 : 128;
+  signed char true_live = one ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_live = zero ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+constexpr bool is_zero(int value) { return value == 0; }
+
+void constexpr_conditions(int condition) {
+  // Test constexpr conditions independently of constexpr destinations.
+  constexpr bool never = false;
+  constexpr bool always = true;
+  signed char variable_true_dead = never ? 128 : 1;
+  signed char variable_false_dead = always ? 1 : 128;
+  signed char variable_true_live = always ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char variable_false_live = never ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+  signed char function_true_dead = is_zero(1) ? 128 : 1;
+  signed char function_false_dead = is_zero(2 - 2) ? 1 : 128;
+  signed char function_true_live = is_zero(0) ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char function_false_live = is_zero(1 + 1) ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+  // A constexpr function can also be called with a runtime argument.
+  signed char function_true_maybe = is_zero(condition) ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char function_false_maybe = is_zero(condition) ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+void local_initializers(bool condition) {
+  constexpr signed char constexpr_dead = false ? 128 : 1;
+  constexpr signed char constexpr_live = true ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'const signed 
char' changes value from 128 to -128}}
+
+  short short_dead = false ? 32768 : 1;
+  int int_dead = false ? 2147483648LL : 1;
+
+  short short_live = true ? 32768 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'short' changes 
value from 32768 to -32768}}
+  int int_maybe = condition ? 2147483648LL : 1;
+  // expected-warning@-1 {{implicit conversion from 'long long' to 'int' 
changes value from 2147483648 to -2147483648}}
+
+  signed char nested_dead = true ? 1 : ((true ? 128 : 1) + 0);
+  signed char nested_live = false ? 1 : ((true ? 128 : 1) + 0);
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+constexpr signed char global_dead = false ? 128 : 1;
+constexpr signed char global_live = true ? 128 : 1;
+// expected-warning@-1 {{implicit conversion from 'int' to 'const signed char' 
changes value from 128 to -128}}
+
+struct MemberInitializers {
+  signed char member_dead = false ? 128 : 1;
+  signed char member_live = true ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+  static const signed char static_dead = false ? 128 : 1;
+  static const signed char static_live = true ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'const signed 
char' changes value from 128 to -128}}
+};
+
+void default_arg_dead(signed char = false ? 128 : 1);
+void default_arg_live(signed char = true ? 128 : 1);
+// expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+// The larger truncation path must also respect the unselected operand, even
+// in contexts where there is no function body to analyze for reachability.
+void default_arg_truncation_dead(signed char = false ? 256 : 1);
+void default_arg_truncation_live(signed char = true ? 256 : 1);
+// expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 256 to 0}}
+
+int consume(signed char);
+signed char sink;
+int total;
+
+// Preserve the outer conditional's reachability when recursing through
+// calls, comparisons, assignments, and compound assignments.
+void call_dead(int = false ? consume(128) : 1);
+void call_live(int = true ? consume(128) : 1);
+// expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+void comparison_dead(bool = false ? (consume(128) == 0) : false);
+void comparison_live(bool = true ? (consume(128) == 0) : false);
+// expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+void assignment_dead(int = false ? (sink = 128) : 1);
+void assignment_live(int = true ? (sink = 128) : 1);
+// expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+void compound_assignment_dead(int = false ? (total += consume(128)) : 1);
+void compound_assignment_live(int = true ? (total += consume(128)) : 1);
+// expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+
+struct NestedInitializers {
+  bool comparison_dead = false ? (consume(128) == 0) : false;
+  int assignment_dead = false ? (sink = 128) : 1;
+  int compound_assignment_dead = false ? (total += consume(128)) : 1;
+  signed char truncation_dead = false ? 256 : 1;
+  signed char truncation_live = true ? 256 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 256 to 0}}
+};
+
+constexpr bool in_constant_evaluation() {
+  return __builtin_is_constant_evaluated();
+}
+
+// Runtime reachability must not suppress conversions that take place during
+// constant evaluation.
+constexpr signed char constant_global_live = in_constant_evaluation() ? 128 : 
1;
+// expected-warning@-1 {{implicit conversion from 'int' to 'const signed char' 
changes value from 128 to -128}}
+constexpr signed char constant_global_dead = in_constant_evaluation() ? 1 : 
128;
+static_assert(constant_global_live == -128, "");
+static_assert(constant_global_dead == 1, "");
+
+void constant_initializers() {
+  constexpr signed char live = in_constant_evaluation() ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'const signed 
char' changes value from 128 to -128}}
+  constexpr signed char dead = in_constant_evaluation() ? 1 : 128;
+  static_assert(live == -128, "");
+  static_assert(dead == 1, "");
+}
+
+// Neither operand can be ruled out when the same function can be evaluated
+// both at runtime and at compile time.
+constexpr signed char constant_function() {
+  return in_constant_evaluation() ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+constexpr signed char runtime_function() {
+  return in_constant_evaluation() ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+static_assert(constant_function() == -128, "");
+static_assert(runtime_function() == 1, "");
+
+constexpr int zero(signed char) { return 0; }
+
+void binary_conditionals() {
+  // The common expression of GNU's x ?: y is evaluated even when it is zero.
+  int common_live = zero(128) ?: 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  int common_dead = false ? (zero(128) ?: 1) : 1;
+
+  signed char true_live = 128 ?: 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_live = 0 ?: 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_dead = 1 ?: 128;
+}
+
+template <bool Condition> void template_conditional() {
+  signed char value = Condition ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+template <bool Condition> void template_false_operand() {
+  signed char value = Condition ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+template <typename T> void sizeof_condition() {
+  signed char true_operand = sizeof(T) == 1 ? 128 : 1;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+  signed char false_operand = sizeof(T) == 1 ? 1 : 128;
+  // expected-warning@-1 {{implicit conversion from 'int' to 'signed char' 
changes value from 128 to -128}}
+}
+
+void instantiate_conditionals() {
+  template_conditional<false>();
+  template_conditional<true>();
+  // expected-note@-1 {{in instantiation of function template specialization 
'GH223923::template_conditional<true>' requested here}}
+  template_false_operand<true>();
+  template_false_operand<false>();
+  // expected-note@-1 {{in instantiation of function template specialization 
'GH223923::template_false_operand<false>' requested here}}
+  sizeof_condition<char>();
+  // expected-note@-1 {{in instantiation of function template specialization 
'GH223923::sizeof_condition<char>' requested here}}
+  sizeof_condition<int>();
+  // expected-note@-1 {{in instantiation of function template specialization 
'GH223923::sizeof_condition<int>' requested here}}
+}
+
+// Suppressing constant-conversion diagnostics must not skip other checks in
+// unselected operands.
+int unrelated_diagnostic(bool b) {
+  return false ? ~b : 0;
+  // expected-warning@-1 {{bitwise negation of a boolean expression}}
+}
+
+} // namespace GH223923
+
 void test_bitfield() {
   struct S {
     int one_bit : 1;

>From 21421b1f003793fc00059e5c13d20e1c3d553c5a Mon Sep 17 00:00:00 2001
From: Alex Filak <[email protected]>
Date: Fri, 18 Sep 2026 22:59:21 +0300
Subject: [PATCH 3/3] [clang][docs] Document the conditional conversion warning
 fix

---
 clang/docs/ReleaseNotes.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6acf353f93f6..9a3962c6224139 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -503,6 +503,10 @@ features cannot lower the translation-unit ABI level;
 
 - Fixed a missing `-Wconstant-conversion` diagnostic for signed `char` arrays.
 
+- Fixed false-positive `-Wconstant-conversion` diagnostics for values in
+  unreachable branches of conditional expressions, including signed `char`
+  array initializers. (#GH223923)
+
 - Clang now diagnoses passing wrong vector type as a mask to 
`__builtin_shufflevector`. (#GH218132)
 
 - `-Wdelete-abstract-non-virtual-dtor` and 
`-Wdelete-non-abstract-non-virtual-dtor`

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to