[clang] [Clang][C++23] Implement P1774R8: Portable assumptions (PR #81014)

via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 7 08:25:42 PST 2024


https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/81014

>From 5ba669011752b6ea6e6fe5f6141c4af66ca7ccbf Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 17:25:10 +0100
Subject: [PATCH 01/27] [Clang] Parsing and Sema for C++23's `assume` attribute

---
 clang/include/clang/Basic/Attr.td             |  9 ++++
 clang/include/clang/Basic/DiagnosticGroups.td |  4 +-
 .../clang/Basic/DiagnosticParseKinds.td       |  3 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  7 +++
 clang/include/clang/Parse/Parser.h            |  7 +++
 clang/lib/Parse/ParseDeclCXX.cpp              | 54 ++++++++++++++++++-
 clang/lib/Parse/ParseExpr.cpp                 | 13 +++++
 clang/lib/Sema/SemaStmtAttr.cpp               | 34 ++++++++++++
 clang/test/Parser/cxx23-assume.cpp            | 27 ++++++++++
 9 files changed, 156 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Parser/cxx23-assume.cpp

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b2d5309e142c1a..2804ff1aea005f 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1564,6 +1564,15 @@ def Unlikely : StmtAttr {
 }
 def : MutualExclusions<[Likely, Unlikely]>;
 
+def Assume : StmtAttr {
+  let Spellings = [CXX11<"", "assume", 202302>];
+  let Subjects = SubjectList<[NullStmt], ErrorDiag, "empty statements">;
+  // The standard only allows a conditional-expression here, but we ought
+  // to get better results by handling that in Sema.
+  let Args = [ExprArgument<"Assumption">];
+  let Documentation = [AssumptionDocs];
+}
+
 def NoMerge : DeclOrStmtAttr {
   let Spellings = [Clang<"nomerge">];
   let Documentation = [NoMergeDocs];
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 6765721ae7002c..192b081404a827 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1124,9 +1124,11 @@ def NonGCC : DiagGroup<"non-gcc",
 def CXX14Attrs : DiagGroup<"c++14-attribute-extensions">;
 def CXX17Attrs : DiagGroup<"c++17-attribute-extensions">;
 def CXX20Attrs : DiagGroup<"c++20-attribute-extensions">;
+def CXX23Attrs : DiagGroup<"c++23-attribute-extensions">;
 def FutureAttrs : DiagGroup<"future-attribute-extensions", [CXX14Attrs,
                                                             CXX17Attrs,
-                                                            CXX20Attrs]>;
+                                                            CXX20Attrs,
+                                                            CXX23Attrs]>;
 
 def CXX23AttrsOnLambda : DiagGroup<"c++23-lambda-attributes">;
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index a30ab27566ec3e..9ecfdab3617e05 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -783,6 +783,9 @@ def err_ms_property_expected_comma_or_rparen : Error<
 def err_ms_property_initializer : Error<
   "property declaration cannot have a default member initializer">;
 
+def err_assume_attr_expects_cond_expr : Error<
+  "use of this expression in an 'assume' attribute requires parentheses">;
+
 def warn_cxx20_compat_explicit_bool : Warning<
   "this expression will be parsed as explicit(bool) in C++20">,
   InGroup<CXX20Compat>, DefaultIgnore;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b4dc4feee8e63a..847168af288622 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9083,6 +9083,8 @@ def ext_cxx17_attr : Extension<
   "use of the %0 attribute is a C++17 extension">, InGroup<CXX17Attrs>;
 def ext_cxx20_attr : Extension<
   "use of the %0 attribute is a C++20 extension">, InGroup<CXX20Attrs>;
+def ext_cxx23_attr : Extension<
+  "use of the %0 attribute is a C++23 extension">, InGroup<CXX23Attrs>;
 
 def warn_unused_comparison : Warning<
   "%select{equality|inequality|relational|three-way}0 comparison result unused">,
@@ -10149,6 +10151,11 @@ def err_fallthrough_attr_outside_switch : Error<
 def err_fallthrough_attr_invalid_placement : Error<
   "fallthrough annotation does not directly precede switch label">;
 
+def err_assume_attr_args : Error<
+  "attribute 'assume' requires a single expression argument">;
+def err_assume_attr_wrong_target : Error<
+  "'assume' attribute is only allowed on empty statements">;
+
 def warn_unreachable_default : Warning<
   "default label in switch which covers all enumeration values">,
   InGroup<CoveredSwitchDefault>, DefaultIgnore;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index da18cf88edcc92..0f982dbb67b41c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1801,6 +1801,7 @@ class Parser : public CodeCompletionHandler {
   ExprResult ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause);
   // Expr that doesn't include commas.
   ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
+  ExprResult ParseConditionalExpression();
 
   ExprResult ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
                                   unsigned &NumLineToksConsumed,
@@ -2953,6 +2954,12 @@ class Parser : public CodeCompletionHandler {
                                SourceLocation ScopeLoc,
                                CachedTokens &OpenMPTokens);
 
+  /// Parse a C++23 assume() attribute. Returns true on error.
+  bool ParseAssumeAttributeArg(ParsedAttributes &Attrs,
+                               IdentifierInfo *AttrName,
+                               SourceLocation AttrNameLoc,
+                               SourceLocation *EndLoc);
+
   IdentifierInfo *TryParseCXX11AttributeIdentifier(
       SourceLocation &Loc,
       Sema::AttributeCompletion Completion = Sema::AttributeCompletion::None,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 79928ddb5af599..768aca68030fe2 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4528,6 +4528,53 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
   }
 }
 
+/// Parse the argument to C++23's [[assume()]] attribute.
+bool Parser::ParseAssumeAttributeArg(ParsedAttributes &Attrs,
+                                     IdentifierInfo *AttrName,
+                                     SourceLocation AttrNameLoc,
+                                     SourceLocation *EndLoc) {
+  assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
+  BalancedDelimiterTracker T(*this, tok::l_paren);
+  T.consumeOpen();
+
+  EnterExpressionEvaluationContext Unevaluated(
+      Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+
+  TentativeParsingAction TPA(*this);
+  ExprResult Res(
+      Actions.CorrectDelayedTyposInExpr(ParseConditionalExpression()));
+  if (Res.isInvalid() || !Tok.is(tok::r_paren)) {
+    // Emit a better diagnostic if this is an otherwise valid expression that
+    // is not allowed here.
+    TPA.Revert();
+    Sema::TentativeAnalysisScope Scope(Actions);
+    Res = ParseExpression();
+    if (!Res.isInvalid()) {
+      auto *E = Res.get();
+      Diag(E->getExprLoc(), diag::err_assume_attr_expects_cond_expr)
+          << AttrName << FixItHint::CreateInsertion(E->getBeginLoc(), "(")
+          << FixItHint::CreateInsertion(PP.getLocForEndOfToken(E->getEndLoc()),
+                                        ")")
+          << E->getSourceRange();
+    }
+
+    T.consumeClose();
+    return true;
+  }
+
+  TPA.Commit();
+  ArgsUnion Assumption = Res.get();
+  auto RParen = Tok.getLocation();
+  T.consumeClose();
+  Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), nullptr,
+               SourceLocation(), &Assumption, 1, ParsedAttr::Form::CXX11());
+
+  if (EndLoc)
+    *EndLoc = RParen;
+
+  return false;
+}
+
 /// ParseCXX11AttributeArgs -- Parse a C++11 attribute-argument-clause.
 ///
 /// [C++11] attribute-argument-clause:
@@ -4596,7 +4643,12 @@ bool Parser::ParseCXX11AttributeArgs(
   if (ScopeName && (ScopeName->isStr("clang") || ScopeName->isStr("_Clang")))
     NumArgs = ParseClangAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc,
                                       ScopeName, ScopeLoc, Form);
-  else
+  // So does C++23's assume() attribute.
+  else if (!ScopeName && AttrName->isStr("assume")) {
+    if (ParseAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc))
+      return true;
+    NumArgs = 1;
+  } else
     NumArgs = ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc,
                                        ScopeName, ScopeLoc, Form);
 
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 52cebdb6f64bac..cf2a7bd026c5de 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -179,6 +179,19 @@ ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
   return ParseRHSOfBinaryExpression(LHS, prec::Assignment);
 }
 
+ExprResult Parser::ParseConditionalExpression() {
+  if (Tok.is(tok::code_completion)) {
+    cutOffParsing();
+    Actions.CodeCompleteExpression(getCurScope(),
+                                   PreferredType.get(Tok.getLocation()));
+    return ExprError();
+  }
+
+  ExprResult LHS = ParseCastExpression(
+      AnyCastExpr, /*isAddressOfOperand=*/false, NotTypeCast);
+  return ParseRHSOfBinaryExpression(LHS, prec::Conditional);
+}
+
 /// Parse an assignment expression where part of an Objective-C message
 /// send has already been parsed.
 ///
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index e6a4d3e63e4aa8..41f6461e183e76 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -303,6 +303,38 @@ static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
   return ::new (S.Context) AlwaysInlineAttr(S.Context, A);
 }
 
+static Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
+                              SourceRange Range) {
+  if (!S.getLangOpts().CPlusPlus23)
+    S.Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;
+
+  if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {
+    S.Diag(A.getLoc(), diag::err_assume_attr_args) << Range;
+    return nullptr;
+  }
+
+  if (!isa<NullStmt>(St)) {
+    S.Diag(A.getLoc(), diag::err_assume_attr_wrong_target) << Range;
+    return nullptr;
+  }
+
+  auto *Assumption = A.getArgAsExpr(0);
+  if (Assumption->getDependence() == ExprDependence::None) {
+    ExprResult Res = S.CorrectDelayedTyposInExpr(Assumption);
+    if (Res.isInvalid())
+      return nullptr;
+    Res = S.CheckPlaceholderExpr(Res.get());
+    if (Res.isInvalid())
+      return nullptr;
+    Res = S.PerformContextuallyConvertToBool(Res.get());
+    if (Res.isInvalid())
+      return nullptr;
+    Assumption = Res.get();
+  }
+
+  return ::new (S.Context) AssumeAttr(S.Context, A, Assumption);
+}
+
 static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,
                                 SourceRange Range) {
   // Validation is in Sema::ActOnAttributedStmt().
@@ -594,6 +626,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
   switch (A.getKind()) {
   case ParsedAttr::AT_AlwaysInline:
     return handleAlwaysInlineAttr(S, St, A, Range);
+  case ParsedAttr::AT_Assume:
+    return handleAssumeAttr(S, St, A, Range);
   case ParsedAttr::AT_FallThrough:
     return handleFallThroughAttr(S, St, A, Range);
   case ParsedAttr::AT_LoopHint:
diff --git a/clang/test/Parser/cxx23-assume.cpp b/clang/test/Parser/cxx23-assume.cpp
new file mode 100644
index 00000000000000..ecf6754600ccb8
--- /dev/null
+++ b/clang/test/Parser/cxx23-assume.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++23 -x c++ %s -verify
+
+struct A{};
+struct B{ explicit operator bool() { return true; } };
+
+void f(int x, int y) {
+  [[assume(true)]];
+  [[assume(1)]];
+  [[assume(1.0)]];
+  [[assume(1 + 2 == 3)]];
+  [[assume((1, 2, 3))]];
+  [[assume((x = 3))]];
+  [[assume(x ? 1 : 2)]];
+  [[assume(x && y)]];
+  [[assume(x++)]];
+  [[assume(++x)]];
+  [[assume([]{ return true; }())]];
+  [[assume(B{})]];
+  [[assume(true)]] [[assume(true)]];
+
+  [[assume]]; // expected-error {{takes one argument}}
+  [[assume(z)]]; // expected-error {{undeclared identifier}}
+  [[assume(x = 2)]]; // expected-error {{requires parentheses}}
+  [[assume(2, 3)]]; // expected-error {{requires parentheses}}
+  [[assume(A{})]]; // expected-error {{not contextually convertible to 'bool'}}
+  [[assume(true)]] if (true) {} // expected-error {{only applies to empty statements}}
+}
\ No newline at end of file

>From df08a1468bda9337dcd602472fee32f1bd953f87 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 17:42:30 +0100
Subject: [PATCH 02/27] [Clang] Make argument of assume() potentially evaluated

---
 clang/lib/Parse/ParseDeclCXX.cpp    |  3 ++-
 clang/test/Parser/cxx23-assume.cpp  |  8 +++++++-
 clang/test/SemaCXX/cxx23-assume.cpp | 26 ++++++++++++++++++++++++++
 3 files changed, 35 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaCXX/cxx23-assume.cpp

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 768aca68030fe2..7f2762d4fe22b9 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4537,8 +4537,9 @@ bool Parser::ParseAssumeAttributeArg(ParsedAttributes &Attrs,
   BalancedDelimiterTracker T(*this, tok::l_paren);
   T.consumeOpen();
 
+  // [dcl.attr.assume]: The expression is potentially evaluated.
   EnterExpressionEvaluationContext Unevaluated(
-      Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+      Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
 
   TentativeParsingAction TPA(*this);
   ExprResult Res(
diff --git a/clang/test/Parser/cxx23-assume.cpp b/clang/test/Parser/cxx23-assume.cpp
index ecf6754600ccb8..5f14a351445543 100644
--- a/clang/test/Parser/cxx23-assume.cpp
+++ b/clang/test/Parser/cxx23-assume.cpp
@@ -8,7 +8,6 @@ void f(int x, int y) {
   [[assume(1)]];
   [[assume(1.0)]];
   [[assume(1 + 2 == 3)]];
-  [[assume((1, 2, 3))]];
   [[assume((x = 3))]];
   [[assume(x ? 1 : 2)]];
   [[assume(x && y)]];
@@ -18,10 +17,17 @@ void f(int x, int y) {
   [[assume(B{})]];
   [[assume(true)]] [[assume(true)]];
 
+  [[assume((1, 2))]]; // expected-warning {{has no effect}}
+
   [[assume]]; // expected-error {{takes one argument}}
   [[assume(z)]]; // expected-error {{undeclared identifier}}
   [[assume(x = 2)]]; // expected-error {{requires parentheses}}
   [[assume(2, 3)]]; // expected-error {{requires parentheses}}
   [[assume(A{})]]; // expected-error {{not contextually convertible to 'bool'}}
   [[assume(true)]] if (true) {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] for (;false;) {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] while (false) {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] label:; // expected-error {{cannot be applied to a declaration}}
+  [[assume(true)]] goto label; // expected-error {{only applies to empty statements}}
 }
\ No newline at end of file
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
new file mode 100644
index 00000000000000..9fdcda31947e75
--- /dev/null
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -std=c++23 -x c++ %s -verify
+// RUN: %clang_cc1 -std=c++20 -pedantic -x c++ %s -verify=ext
+// expected-no-diagnostics
+
+template <bool cond>
+void f() {
+  [[assume(cond)]]; // ext-warning {{C++23 extension}}
+}
+
+template <bool cond>
+struct S {
+  void f() {
+    [[assume(cond)]]; // ext-warning {{C++23 extension}}
+  }
+};
+
+void g() {
+  f<true>();
+  f<false>();
+  S<true>{}.f();
+  S<false>{}.f();
+}
+
+// Check that 'x' is ODR-used here.
+constexpr int h(int x) { return sizeof([=] { [[assume(x)]]; }); } // ext-warning {{C++23 extension}}
+static_assert(h(4) == sizeof(int));

>From 64345e281cbd0fa0029b36a4ada40dc311b1ba94 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 18:01:36 +0100
Subject: [PATCH 03/27] [Clang] Move most [[assume()]] diags to test/SemaCXX

---
 clang/lib/Sema/SemaStmtAttr.cpp     |  9 ++++++---
 clang/test/Parser/cxx23-assume.cpp  | 19 -------------------
 clang/test/SemaCXX/cxx23-assume.cpp | 28 +++++++++++++++++++++++++---
 3 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 41f6461e183e76..e712745a237c3e 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -305,9 +305,6 @@ static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
 
 static Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
                               SourceRange Range) {
-  if (!S.getLangOpts().CPlusPlus23)
-    S.Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;
-
   if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {
     S.Diag(A.getLoc(), diag::err_assume_attr_args) << Range;
     return nullptr;
@@ -330,8 +327,14 @@ static Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
     if (Res.isInvalid())
       return nullptr;
     Assumption = Res.get();
+
+    if (Assumption->HasSideEffects(S.Context, /*IncludePossibleEffects=*/true))
+      S.Diag(A.getLoc(), diag::warn_assume_side_effects) << A.getAttrName() << Range;
   }
 
+  if (!S.getLangOpts().CPlusPlus23)
+    S.Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;
+
   return ::new (S.Context) AssumeAttr(S.Context, A, Assumption);
 }
 
diff --git a/clang/test/Parser/cxx23-assume.cpp b/clang/test/Parser/cxx23-assume.cpp
index 5f14a351445543..968caae7dab9a0 100644
--- a/clang/test/Parser/cxx23-assume.cpp
+++ b/clang/test/Parser/cxx23-assume.cpp
@@ -1,33 +1,14 @@
 // RUN: %clang_cc1 -std=c++23 -x c++ %s -verify
 
-struct A{};
-struct B{ explicit operator bool() { return true; } };
-
 void f(int x, int y) {
   [[assume(true)]];
   [[assume(1)]];
   [[assume(1.0)]];
   [[assume(1 + 2 == 3)]];
-  [[assume((x = 3))]];
   [[assume(x ? 1 : 2)]];
   [[assume(x && y)]];
-  [[assume(x++)]];
-  [[assume(++x)]];
-  [[assume([]{ return true; }())]];
-  [[assume(B{})]];
   [[assume(true)]] [[assume(true)]];
 
-  [[assume((1, 2))]]; // expected-warning {{has no effect}}
-
-  [[assume]]; // expected-error {{takes one argument}}
-  [[assume(z)]]; // expected-error {{undeclared identifier}}
   [[assume(x = 2)]]; // expected-error {{requires parentheses}}
   [[assume(2, 3)]]; // expected-error {{requires parentheses}}
-  [[assume(A{})]]; // expected-error {{not contextually convertible to 'bool'}}
-  [[assume(true)]] if (true) {} // expected-error {{only applies to empty statements}}
-  [[assume(true)]] {} // expected-error {{only applies to empty statements}}
-  [[assume(true)]] for (;false;) {} // expected-error {{only applies to empty statements}}
-  [[assume(true)]] while (false) {} // expected-error {{only applies to empty statements}}
-  [[assume(true)]] label:; // expected-error {{cannot be applied to a declaration}}
-  [[assume(true)]] goto label; // expected-error {{only applies to empty statements}}
 }
\ No newline at end of file
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 9fdcda31947e75..d3132c55f4b470 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -std=c++23 -x c++ %s -verify
-// RUN: %clang_cc1 -std=c++20 -pedantic -x c++ %s -verify=ext
-// expected-no-diagnostics
+// RUN: %clang_cc1 -std=c++20 -pedantic -x c++ %s -verify=ext,expected
+
+struct A{};
+struct B{ explicit operator bool() { return true; } };
 
 template <bool cond>
 void f() {
@@ -14,11 +16,31 @@ struct S {
   }
 };
 
-void g() {
+bool f2();
+
+void g(int x) {
   f<true>();
   f<false>();
   S<true>{}.f();
   S<false>{}.f();
+  [[assume(f2())]]; // expected-warning {{side effects that will be discarded}} ext-warning {{C++23 extension}}
+
+  [[assume((x = 3))]]; // expected-warning {{has side effects that will be discarded}} // ext-warning {{C++23 extension}}
+  [[assume(x++)]]; // expected-warning {{has side effects that will be discarded}} // ext-warning {{C++23 extension}}
+  [[assume(++x)]]; // expected-warning {{has side effects that will be discarded}} // ext-warning {{C++23 extension}}
+  [[assume([]{ return true; }())]]; // expected-warning {{has side effects that will be discarded}} // ext-warning {{C++23 extension}}
+  [[assume(B{})]]; // expected-warning {{has side effects that will be discarded}} // ext-warning {{C++23 extension}}
+  [[assume((1, 2))]]; // expected-warning {{has no effect}} // ext-warning {{C++23 extension}}
+
+  [[assume]]; // expected-error {{takes one argument}}
+  [[assume(z)]]; // expected-error {{undeclared identifier}}
+  [[assume(A{})]]; // expected-error {{not contextually convertible to 'bool'}}
+  [[assume(true)]] if (true) {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] for (;false;) {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] while (false) {} // expected-error {{only applies to empty statements}}
+  [[assume(true)]] label:; // expected-error {{cannot be applied to a declaration}}
+  [[assume(true)]] goto label; // expected-error {{only applies to empty statements}}
 }
 
 // Check that 'x' is ODR-used here.

>From 6c44d2097b2e1594857f29437f09def1c3e1b0d9 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 18:11:10 +0100
Subject: [PATCH 04/27] [Clang] Codegen for [[assume()]]

---
 clang/lib/CodeGen/CGStmt.cpp           | 11 ++++++++--
 clang/test/CodeGenCXX/cxx23-assume.cpp | 28 ++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/cxx23-assume.cpp

diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index beff0ad9da2709..66be305550b1b9 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -721,11 +721,18 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
     case attr::AlwaysInline:
       alwaysinline = true;
       break;
-    case attr::MustTail:
+    case attr::MustTail: {
       const Stmt *Sub = S.getSubStmt();
       const ReturnStmt *R = cast<ReturnStmt>(Sub);
       musttail = cast<CallExpr>(R->getRetValue()->IgnoreParens());
-      break;
+    } break;
+    case attr::Assume: {
+      const Expr *Assumption = cast<AssumeAttr>(A)->getAssumption();
+      if (!Assumption->HasSideEffects(getContext())) {
+        llvm::Value *AssumptionVal = EvaluateExprAsBool(Assumption);
+        Builder.CreateAssumption(AssumptionVal);
+      }
+    } break;
     }
   }
   SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge);
diff --git a/clang/test/CodeGenCXX/cxx23-assume.cpp b/clang/test/CodeGenCXX/cxx23-assume.cpp
new file mode 100644
index 00000000000000..8a8438e8edc057
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx23-assume.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 %s -emit-llvm -o - | FileCheck %s
+
+bool f();
+
+// CHECK: @_Z1gii(i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]])
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32
+// CHECK-NEXT: [[Y_ADDR:%.*]] = alloca i32
+// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]]
+// CHECK-NEXT: store i32 [[Y]], ptr [[Y_ADDR]]
+void g(int x, int y) {
+  // Not emitted because it has side-effects.
+  [[assume(f())]];
+
+  // CHECK-NEXT: call void @llvm.assume(i1 true)
+  [[assume((1, 2))]];
+
+  // [[X1:%.*]] = load i32, ptr [[X_ADDR]]
+  // [[CMP1:%.*]] = icmp ne i32 [[X1]], 27
+  // call void @llvm.assume(i1 [[CMP1]])
+  [[assume(x != 27)]];
+
+  // [[X2:%.*]] = load i32, ptr [[X_ADDR]]
+  // [[Y2:%.*]] = load i32, ptr [[Y_ADDR]]
+  // [[CMP2:%.*]] = icmp eq i32 [[X2]], [[Y2]]
+  // call void @llvm.assume(i1 [[CMP2]])
+  [[assume(x == y)]];
+}

>From dd8febdf4858c72194da2dea6460256e40816b1f Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 18:14:21 +0100
Subject: [PATCH 05/27] [Clang] Check that __has_cpp_attribute returns 1 for
 assume

---
 clang/test/SemaCXX/cxx23-assume.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index d3132c55f4b470..15ea831f77f10d 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -46,3 +46,5 @@ void g(int x) {
 // Check that 'x' is ODR-used here.
 constexpr int h(int x) { return sizeof([=] { [[assume(x)]]; }); } // ext-warning {{C++23 extension}}
 static_assert(h(4) == sizeof(int));
+
+static_assert(__has_cpp_attribute(assume));

>From b498be8971e945bff3592f76f162c1ed8d0abb4d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 18:18:47 +0100
Subject: [PATCH 06/27] [NFC] Add newline at end of file

---
 clang/test/Parser/cxx23-assume.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Parser/cxx23-assume.cpp b/clang/test/Parser/cxx23-assume.cpp
index 968caae7dab9a0..2a9b8b6a248821 100644
--- a/clang/test/Parser/cxx23-assume.cpp
+++ b/clang/test/Parser/cxx23-assume.cpp
@@ -11,4 +11,4 @@ void f(int x, int y) {
 
   [[assume(x = 2)]]; // expected-error {{requires parentheses}}
   [[assume(2, 3)]]; // expected-error {{requires parentheses}}
-}
\ No newline at end of file
+}

>From 36fc751a4b3d55798f0e215bdb92f10e0d82afca Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 18:31:04 +0100
Subject: [PATCH 07/27] [Clang] Update documentation

---
 clang/docs/ReleaseNotes.rst           |  1 +
 clang/include/clang/Basic/Attr.td     |  2 +-
 clang/include/clang/Basic/AttrDocs.td | 24 ++++++++++++++++++++++++
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 802c44b6c86080..6312e100913613 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -95,6 +95,7 @@ C++23 Feature Support
 
 - Implemented `P2718R0: Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_. Also
   materialize temporary object which is a prvalue in discarded-value expression.
+- Implemented `P1774R8: Portable assumptions <https://wg21.link/P1774R8>`_.
 
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 2804ff1aea005f..6cd2a541da17d9 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1570,7 +1570,7 @@ def Assume : StmtAttr {
   // The standard only allows a conditional-expression here, but we ought
   // to get better results by handling that in Sema.
   let Args = [ExprArgument<"Assumption">];
-  let Documentation = [AssumptionDocs];
+  let Documentation = [AssumeDocs];
 }
 
 def NoMerge : DeclOrStmtAttr {
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 041786f37fb8a7..1b27a46ef1d901 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1996,6 +1996,30 @@ Here is an example:
   }];
 }
 
+def AssumeDocs : Documentation {
+  let Category = DocCatStmt;
+  let Heading = "assume";
+  let Content = [{
+The ``assume`` attribute is used to indicate to the optimizer that a
+certain condition can be assumed to be true at a certain point in the
+program. If this condition is violated at runtime, the behavior is
+undefined. ``assume`` can only be applied to a null statement.
+
+Note that `clang::assume` is a different attribute. Always write ``assume``
+without a namespace if you intend to use the standard C++ attribute.
+
+Example:
+
+.. code-block:: c++
+
+  int f(int x, int y) {
+    [[assume(x == 27)]];
+    [[assume(x == y)]];
+    return y + 1; // Will be optimised to `return 28`.
+  }
+  }];
+}
+
 def LikelihoodDocs : Documentation {
   let Category = DocCatStmt;
   let Heading = "likely and unlikely";

>From fc720294e740581c38fc532560c9ad6aece626a4 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 18:37:43 +0100
Subject: [PATCH 08/27] [Clang][NFC] Remove outdated comment

---
 clang/include/clang/Basic/Attr.td | 2 --
 1 file changed, 2 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 6cd2a541da17d9..d5810221b633b8 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1567,8 +1567,6 @@ def : MutualExclusions<[Likely, Unlikely]>;
 def Assume : StmtAttr {
   let Spellings = [CXX11<"", "assume", 202302>];
   let Subjects = SubjectList<[NullStmt], ErrorDiag, "empty statements">;
-  // The standard only allows a conditional-expression here, but we ought
-  // to get better results by handling that in Sema.
   let Args = [ExprArgument<"Assumption">];
   let Documentation = [AssumeDocs];
 }

>From 0d869a5478b06fb7ae2eadc71034ed3530c9ebc5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 18:40:40 +0100
Subject: [PATCH 09/27] [Clang][NFC] Fix formatting

---
 clang/lib/Sema/SemaStmtAttr.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index e712745a237c3e..0812bc8761c9a0 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -329,7 +329,8 @@ static Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
     Assumption = Res.get();
 
     if (Assumption->HasSideEffects(S.Context, /*IncludePossibleEffects=*/true))
-      S.Diag(A.getLoc(), diag::warn_assume_side_effects) << A.getAttrName() << Range;
+      S.Diag(A.getLoc(), diag::warn_assume_side_effects)
+          << A.getAttrName() << Range;
   }
 
   if (!S.getLangOpts().CPlusPlus23)

>From 59285828115e51ae0008ec94a7afe222324d5baa Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 19:43:58 +0100
Subject: [PATCH 10/27] [Clang] Do not hard-code attribute name in diagnostics

---
 clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +-
 clang/include/clang/Basic/DiagnosticSemaKinds.td  | 4 ++--
 clang/lib/Sema/SemaStmtAttr.cpp                   | 5 +++--
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 9ecfdab3617e05..351c9eeca2de40 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -784,7 +784,7 @@ def err_ms_property_initializer : Error<
   "property declaration cannot have a default member initializer">;
 
 def err_assume_attr_expects_cond_expr : Error<
-  "use of this expression in an 'assume' attribute requires parentheses">;
+  "use of this expression in an '%0' attribute requires parentheses">;
 
 def warn_cxx20_compat_explicit_bool : Warning<
   "this expression will be parsed as explicit(bool) in C++20">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 847168af288622..b9f0d1fc81e0db 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10152,9 +10152,9 @@ def err_fallthrough_attr_invalid_placement : Error<
   "fallthrough annotation does not directly precede switch label">;
 
 def err_assume_attr_args : Error<
-  "attribute 'assume' requires a single expression argument">;
+  "attribute '%0' requires a single expression argument">;
 def err_assume_attr_wrong_target : Error<
-  "'assume' attribute is only allowed on empty statements">;
+  "'%0' attribute is only allowed on empty statements">;
 
 def warn_unreachable_default : Warning<
   "default label in switch which covers all enumeration values">,
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 0812bc8761c9a0..5f22fcd4ff0acc 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -306,12 +306,13 @@ static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
 static Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
                               SourceRange Range) {
   if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {
-    S.Diag(A.getLoc(), diag::err_assume_attr_args) << Range;
+    S.Diag(A.getLoc(), diag::err_assume_attr_args) << A.getAttrName() << Range;
     return nullptr;
   }
 
   if (!isa<NullStmt>(St)) {
-    S.Diag(A.getLoc(), diag::err_assume_attr_wrong_target) << Range;
+    S.Diag(A.getLoc(), diag::err_assume_attr_wrong_target)
+        << A.getAttrName() << Range;
     return nullptr;
   }
 

>From f286e2e3c62f7493db5395ff61d4503afad03bb5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 20:12:28 +0100
Subject: [PATCH 11/27] [Clang] Diagnose failed assumptions during constant
 expression evaluation

---
 clang/include/clang/Basic/DiagnosticASTKinds.td |  2 ++
 clang/lib/AST/ExprConstant.cpp                  | 13 +++++++++++++
 clang/test/SemaCXX/cxx23-assume.cpp             | 15 +++++++++++++++
 3 files changed, 30 insertions(+)

diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index c81d17ed641084..a024f9b2a9f8c0 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -399,6 +399,8 @@ def note_constexpr_unsupported_flexible_array : Note<
   "flexible array initialization is not yet supported">;
 def note_constexpr_non_const_vectorelements : Note<
   "cannot determine number of elements for sizeless vectors in a constant expression">;
+def note_constexpr_assumption_failed : Note<
+  "assumption evaluated to false">;
 def err_experimental_clang_interp_failed : Error<
   "the experimental clang interpreter failed to evaluate an expression">;
 
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 089bc2094567f7..7a61273fccc7a0 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5569,6 +5569,19 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
     MSConstexprContextRAII ConstexprContext(
         *Info.CurrentCall, hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
                                isa<ReturnStmt>(SS));
+
+    for (auto *Attr : AS->getAttrs()) {
+      auto *Assume = dyn_cast<AssumeAttr>(Attr);
+      if (!Assume) continue;
+      bool Value;
+      if (!EvaluateAsBooleanCondition(Assume->getAssumption(), Value, Info))
+        return ESR_Failed;
+      if (!Value) {
+        Info.CCEDiag(Assume->getAssumption()->getExprLoc(), diag::note_constexpr_assumption_failed);
+        return ESR_Failed;
+      }
+    }
+
     return EvaluateStmt(Result, Info, SS, Case);
   }
 
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 15ea831f77f10d..6e8fe812e9df20 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -48,3 +48,18 @@ constexpr int h(int x) { return sizeof([=] { [[assume(x)]]; }); } // ext-warning
 static_assert(h(4) == sizeof(int));
 
 static_assert(__has_cpp_attribute(assume));
+static_assert(__has_attribute(assume));
+
+constexpr bool i() { // expected-error {{never produces a constant expression}}
+  [[assume(false)]]; // expected-note {{assumption evaluated to false}} expected-note {{assumption evaluated to false}} ext-warning {{C++23 extension}}
+  return true;
+}
+
+constexpr bool j(bool b) {
+  [[assume(b)]]; // expected-note {{assumption evaluated to false}} ext-warning {{C++23 extension}}
+  return true;
+}
+
+static_assert(i()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
+static_assert(j(true));
+static_assert(j(false)); // expected-error {{not an integral constant expression}} expected-note {{in call to}}

>From 2fb786cbdff7fd586a74f716047d2975c7558708 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 7 Feb 2024 20:13:25 +0100
Subject: [PATCH 12/27] [Clang][NFC] Fix formatting

---
 clang/lib/AST/ExprConstant.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 7a61273fccc7a0..716de07f2b770c 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5572,12 +5572,14 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
 
     for (auto *Attr : AS->getAttrs()) {
       auto *Assume = dyn_cast<AssumeAttr>(Attr);
-      if (!Assume) continue;
+      if (!Assume)
+        continue;
       bool Value;
       if (!EvaluateAsBooleanCondition(Assume->getAssumption(), Value, Info))
         return ESR_Failed;
       if (!Value) {
-        Info.CCEDiag(Assume->getAssumption()->getExprLoc(), diag::note_constexpr_assumption_failed);
+        Info.CCEDiag(Assume->getAssumption()->getExprLoc(),
+                     diag::note_constexpr_assumption_failed);
         return ESR_Failed;
       }
     }

>From dc23ed565d54e615585dead856c0df6cfb7a6fa3 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 8 Feb 2024 05:05:20 +0100
Subject: [PATCH 13/27] [Clang] Handle assume attribute in template
 instantiation

---
 clang/include/clang/Sema/Sema.h            |  4 ++
 clang/lib/AST/ExprConstant.cpp             | 13 ++++--
 clang/lib/Sema/SemaExprCXX.cpp             | 50 ++++++++++++++++++++++
 clang/lib/Sema/SemaStmtAttr.cpp            | 38 +++-------------
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 15 +++++++
 clang/test/SemaCXX/cxx23-assume.cpp        | 18 ++++++++
 6 files changed, 101 insertions(+), 37 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3c26003b5bda7f..88303bc6809d4f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5828,6 +5828,10 @@ class Sema final {
 
   bool CheckLoopHintExpr(Expr *E, SourceLocation Loc);
 
+  ExprResult ActOnAssumeAttr(Stmt *St, const ParsedAttr &A, SourceRange Range);
+  ExprResult BuildAssumeExpr(Expr *Assumption, const IdentifierInfo *AttrName,
+                             SourceRange Range);
+
   ExprResult ActOnNumericConstant(const Token &Tok, Scope *UDLScope = nullptr);
   ExprResult ActOnCharacterConstant(const Token &Tok,
                                     Scope *UDLScope = nullptr);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 716de07f2b770c..67053d08e7d496 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5571,14 +5571,19 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
                                isa<ReturnStmt>(SS));
 
     for (auto *Attr : AS->getAttrs()) {
-      auto *Assume = dyn_cast<AssumeAttr>(Attr);
-      if (!Assume)
+      auto *AA = dyn_cast<AssumeAttr>(Attr);
+      if (!AA)
         continue;
+
+      auto *Assumption = AA->getAssumption();
+      if (Assumption->isValueDependent())
+        return ESR_Failed;
+
       bool Value;
-      if (!EvaluateAsBooleanCondition(Assume->getAssumption(), Value, Info))
+      if (!EvaluateAsBooleanCondition(Assumption, Value, Info))
         return ESR_Failed;
       if (!Value) {
-        Info.CCEDiag(Assume->getAssumption()->getExprLoc(),
+        Info.CCEDiag(Assumption->getExprLoc(),
                      diag::note_constexpr_assumption_failed);
         return ESR_Failed;
       }
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 246d2313e089f3..5d888036cafd19 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -9238,3 +9238,53 @@ ExprResult Sema::ActOnRequiresExpr(
     return ExprError();
   return RE;
 }
+
+ExprResult Sema::ActOnAssumeAttr(Stmt *St, const ParsedAttr &A,
+                                 SourceRange Range) {
+  if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {
+    Diag(A.getLoc(), diag::err_assume_attr_args) << A.getAttrName() << Range;
+    return ExprError();
+  }
+
+  if (!isa<NullStmt>(St)) {
+    Diag(A.getLoc(), diag::err_assume_attr_wrong_target)
+        << A.getAttrName() << Range;
+    return ExprError();
+  }
+
+  auto *Assumption = A.getArgAsExpr(0);
+  if (Assumption->getDependence() == ExprDependence::None) {
+    ExprResult Res = BuildAssumeExpr(Assumption, A.getAttrName(), Range);
+    if (Res.isInvalid())
+      return ExprError();
+    Assumption = Res.get();
+  }
+
+  if (!getLangOpts().CPlusPlus23)
+    Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;
+
+  return Assumption;
+}
+
+ExprResult Sema::BuildAssumeExpr(Expr *Assumption,
+                                 const IdentifierInfo *AttrName,
+                                 SourceRange Range) {
+  ExprResult Res = CorrectDelayedTyposInExpr(Assumption);
+  if (Res.isInvalid())
+    return ExprError();
+
+  Res = CheckPlaceholderExpr(Res.get());
+  if (Res.isInvalid())
+    return ExprError();
+
+  Res = PerformContextuallyConvertToBool(Res.get());
+  if (Res.isInvalid())
+    return ExprError();
+
+  Assumption = Res.get();
+  if (Assumption->HasSideEffects(Context))
+    Diag(Assumption->getBeginLoc(), diag::warn_assume_side_effects)
+        << AttrName << Range;
+
+  return Assumption;
+}
\ No newline at end of file
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 5f22fcd4ff0acc..4ebb016804f9d8 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -303,41 +303,13 @@ static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
   return ::new (S.Context) AlwaysInlineAttr(S.Context, A);
 }
 
-static Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
-                              SourceRange Range) {
-  if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {
-    S.Diag(A.getLoc(), diag::err_assume_attr_args) << A.getAttrName() << Range;
+Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
+                       SourceRange Range) {
+  ExprResult Res = S.ActOnAssumeAttr(St, A, Range);
+  if (Res.isInvalid())
     return nullptr;
-  }
-
-  if (!isa<NullStmt>(St)) {
-    S.Diag(A.getLoc(), diag::err_assume_attr_wrong_target)
-        << A.getAttrName() << Range;
-    return nullptr;
-  }
-
-  auto *Assumption = A.getArgAsExpr(0);
-  if (Assumption->getDependence() == ExprDependence::None) {
-    ExprResult Res = S.CorrectDelayedTyposInExpr(Assumption);
-    if (Res.isInvalid())
-      return nullptr;
-    Res = S.CheckPlaceholderExpr(Res.get());
-    if (Res.isInvalid())
-      return nullptr;
-    Res = S.PerformContextuallyConvertToBool(Res.get());
-    if (Res.isInvalid())
-      return nullptr;
-    Assumption = Res.get();
-
-    if (Assumption->HasSideEffects(S.Context, /*IncludePossibleEffects=*/true))
-      S.Diag(A.getLoc(), diag::warn_assume_side_effects)
-          << A.getAttrName() << Range;
-  }
-
-  if (!S.getLangOpts().CPlusPlus23)
-    S.Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;
 
-  return ::new (S.Context) AssumeAttr(S.Context, A, Assumption);
+  return ::new (S.Context) AssumeAttr(S.Context, A, Res.get());
 }
 
 static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 6d59180bc446d2..9bd6b2f494e93a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1410,6 +1410,7 @@ namespace {
                           NamedDecl *FirstQualifierInScope = nullptr,
                           bool AllowInjectedClassName = false);
 
+    const AssumeAttr *TransformAssumeAttr(const AssumeAttr *AA);
     const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
     const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS,
                                                   const Stmt *InstS,
@@ -1926,6 +1927,20 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
                                          Arg, PackIndex);
 }
 
+const AssumeAttr *
+TemplateInstantiator::TransformAssumeAttr(const AssumeAttr *AA) {
+  ExprResult Res = getDerived().TransformExpr(AA->getAssumption());
+  if (Res.isInvalid())
+    return AA;
+
+  Res = getSema().BuildAssumeExpr(Res.get(), AA->getAttrName(), AA->getRange());
+  if (Res.isInvalid())
+    return AA;
+
+  return AssumeAttr::CreateImplicit(getSema().Context, Res.get(),
+                                    AA->getRange());
+}
+
 const LoopHintAttr *
 TemplateInstantiator::TransformLoopHintAttr(const LoopHintAttr *LH) {
   Expr *TransformedExpr = getDerived().TransformExpr(LH->getValue()).get();
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 6e8fe812e9df20..4c6e34c267655e 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -14,15 +14,29 @@ struct S {
   void f() {
     [[assume(cond)]]; // ext-warning {{C++23 extension}}
   }
+
+  template <typename T>
+  constexpr bool g() {
+    [[assume(cond == sizeof(T))]]; // expected-note {{assumption evaluated to false}} ext-warning {{C++23 extension}}
+    return true;
+  }
+
 };
 
 bool f2();
 
+template <typename T>
+constexpr void f3() {
+  [[assume(T{})]]; // expected-error {{not contextually convertible to 'bool'}} expected-warning {{has side effects that will be discarded}} ext-warning {{C++23 extension}}
+}
+
 void g(int x) {
   f<true>();
   f<false>();
   S<true>{}.f();
   S<false>{}.f();
+  S<true>{}.g<char>();
+  S<true>{}.g<int>();
   [[assume(f2())]]; // expected-warning {{side effects that will be discarded}} ext-warning {{C++23 extension}}
 
   [[assume((x = 3))]]; // expected-warning {{has side effects that will be discarded}} // ext-warning {{C++23 extension}}
@@ -32,6 +46,8 @@ void g(int x) {
   [[assume(B{})]]; // expected-warning {{has side effects that will be discarded}} // ext-warning {{C++23 extension}}
   [[assume((1, 2))]]; // expected-warning {{has no effect}} // ext-warning {{C++23 extension}}
 
+  f3<A>(); // expected-note {{in instantiation of}}
+  f3<B>(); // expected-note {{in instantiation of}}
   [[assume]]; // expected-error {{takes one argument}}
   [[assume(z)]]; // expected-error {{undeclared identifier}}
   [[assume(A{})]]; // expected-error {{not contextually convertible to 'bool'}}
@@ -63,3 +79,5 @@ constexpr bool j(bool b) {
 static_assert(i()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
 static_assert(j(true));
 static_assert(j(false)); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
+static_assert(S<true>{}.g<char>());
+static_assert(S<false>{}.g<A>()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}

>From 0725f047c5c90b7b51e68534c9c8c914af43ab12 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 8 Feb 2024 05:51:28 +0100
Subject: [PATCH 14/27] [Clang] Add more tests for assume involving concepts

---
 clang/test/SemaCXX/cxx23-assume.cpp | 49 +++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 4c6e34c267655e..d50cea0a469bc8 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++23 -x c++ %s -verify
+// RUN: %clang_cc1 -std=c++23  -x c++ %s -verify
 // RUN: %clang_cc1 -std=c++20 -pedantic -x c++ %s -verify=ext,expected
 
 struct A{};
@@ -20,7 +20,6 @@ struct S {
     [[assume(cond == sizeof(T))]]; // expected-note {{assumption evaluated to false}} ext-warning {{C++23 extension}}
     return true;
   }
-
 };
 
 bool f2();
@@ -81,3 +80,49 @@ static_assert(j(true));
 static_assert(j(false)); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
 static_assert(S<true>{}.g<char>());
 static_assert(S<false>{}.g<A>()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
+
+
+template <typename T>
+constexpr bool f4() {
+  [[assume(!T{})]]; // expected-error {{invalid argument type 'D'}} // expected-warning 2 {{side effects}} ext-warning {{C++23 extension}}
+  return sizeof(T) == sizeof(int);
+}
+
+template <typename T>
+concept C = f4<T>(); // expected-note 3 {{in instantiation of}}
+                     // expected-note at -1 3 {{while substituting}}
+                     // expected-error at -2 2 {{resulted in a non-constant expression}}
+
+struct D {
+  int x;
+};
+
+struct E {
+  int x;
+  constexpr explicit operator bool() { return false; }
+};
+
+struct F {
+  int x;
+  int y;
+  constexpr explicit operator bool() { return false; }
+};
+
+template <typename T>
+constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking the satisfaction}}
+                                               // expected-note at -1 {{while substituting template arguments}}
+                                               // expected-note at -2 {{candidate template ignored}}
+
+template <typename T>
+constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}}
+                                                  // expected-note at -1 4 {{while substituting template arguments}}
+                                                  // expected-note at -2 {{candidate template ignored}}
+
+static_assert(f5<int>() == 1);
+static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}}
+                             // expected-note at -1 3 {{in instantiation of}}
+                             // expected-error at -2 {{no matching function for call}}
+
+static_assert(f5<double>() == 2);
+static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}}
+static_assert(f5<F>() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}}

>From a46edf9bbe89a69fb66ff63ea1a1265ac6149b46 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 8 Feb 2024 05:58:22 +0100
Subject: [PATCH 15/27] [Clang] Add -ast-print test for assume

---
 clang/test/SemaCXX/cxx23-assume-print.cpp | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 clang/test/SemaCXX/cxx23-assume-print.cpp

diff --git a/clang/test/SemaCXX/cxx23-assume-print.cpp b/clang/test/SemaCXX/cxx23-assume-print.cpp
new file mode 100644
index 00000000000000..1e858ad0956160
--- /dev/null
+++ b/clang/test/SemaCXX/cxx23-assume-print.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++23 -ast-print %s | FileCheck %s
+
+// CHECK: void f(int x, int y) {
+void f(int x, int y) {
+  // CHECK-NEXT: {{\[}}[assume(true)]]
+  [[assume(true)]];
+
+  // CHECK-NEXT: {{\[}}[assume(2 + 4)]]
+  [[assume(2 + 4)]];
+
+  // CHECK-NEXT: {{\[}}[assume(x == y)]]
+  [[assume(x == y)]];
+}
\ No newline at end of file

>From 646b32d2502c46f9212e849be1bf46627e682c7c Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 8 Feb 2024 05:59:49 +0100
Subject: [PATCH 16/27] [Clang][NFC] Add newline at end of file

---
 clang/lib/Sema/SemaExprCXX.cpp            | 2 +-
 clang/test/SemaCXX/cxx23-assume-print.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 5d888036cafd19..6fb4010757a82b 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -9287,4 +9287,4 @@ ExprResult Sema::BuildAssumeExpr(Expr *Assumption,
         << AttrName << Range;
 
   return Assumption;
-}
\ No newline at end of file
+}
diff --git a/clang/test/SemaCXX/cxx23-assume-print.cpp b/clang/test/SemaCXX/cxx23-assume-print.cpp
index 1e858ad0956160..37db015fcc3909 100644
--- a/clang/test/SemaCXX/cxx23-assume-print.cpp
+++ b/clang/test/SemaCXX/cxx23-assume-print.cpp
@@ -10,4 +10,4 @@ void f(int x, int y) {
 
   // CHECK-NEXT: {{\[}}[assume(x == y)]]
   [[assume(x == y)]];
-}
\ No newline at end of file
+}

>From c7515d9a9116c3bc75e972ed618290d3f96226b9 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 8 Feb 2024 06:10:10 +0100
Subject: [PATCH 17/27] [Clang] Actually include CHECK directives in comments

---
 clang/test/CodeGenCXX/cxx23-assume.cpp | 33 ++++++++++++++++++++------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/clang/test/CodeGenCXX/cxx23-assume.cpp b/clang/test/CodeGenCXX/cxx23-assume.cpp
index 8a8438e8edc057..3e8fc522563ba9 100644
--- a/clang/test/CodeGenCXX/cxx23-assume.cpp
+++ b/clang/test/CodeGenCXX/cxx23-assume.cpp
@@ -2,6 +2,11 @@
 
 bool f();
 
+template <typename T>
+void f2() {
+  [[assume(sizeof(T) == sizeof(int))]];
+}
+
 // CHECK: @_Z1gii(i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]])
 // CHECK-NEXT: entry:
 // CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32
@@ -15,14 +20,28 @@ void g(int x, int y) {
   // CHECK-NEXT: call void @llvm.assume(i1 true)
   [[assume((1, 2))]];
 
-  // [[X1:%.*]] = load i32, ptr [[X_ADDR]]
-  // [[CMP1:%.*]] = icmp ne i32 [[X1]], 27
-  // call void @llvm.assume(i1 [[CMP1]])
+  // CHECK-NEXT: [[X1:%.*]] = load i32, ptr [[X_ADDR]]
+  // CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[X1]], 27
+  // CHECK-NEXT: call void @llvm.assume(i1 [[CMP1]])
   [[assume(x != 27)]];
 
-  // [[X2:%.*]] = load i32, ptr [[X_ADDR]]
-  // [[Y2:%.*]] = load i32, ptr [[Y_ADDR]]
-  // [[CMP2:%.*]] = icmp eq i32 [[X2]], [[Y2]]
-  // call void @llvm.assume(i1 [[CMP2]])
+  // CHECK-NEXT: [[X2:%.*]] = load i32, ptr [[X_ADDR]]
+  // CHECK-NEXT: [[Y2:%.*]] = load i32, ptr [[Y_ADDR]]
+  // CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[X2]], [[Y2]]
+  // CHECK-NEXT: call void @llvm.assume(i1 [[CMP2]])
   [[assume(x == y)]];
+
+  // CHECK-NEXT: call void @_Z2f2IiEvv()
+  f2<int>();
+
+  // CHECK-NEXT: call void @_Z2f2IdEvv()
+  f2<double>();
 }
+
+// CHECK: void @_Z2f2IiEvv()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @llvm.assume(i1 true)
+
+// CHECK: void @_Z2f2IdEvv()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @llvm.assume(i1 false)

>From 2f998a4555fad37821b8163dbe5c65c83b5c9e96 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 6 Mar 2024 22:57:40 +0100
Subject: [PATCH 18/27] [Clang] Readd sema functions after merge

---
 clang/include/clang/Sema/Sema.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f3d3a57104ee07..27499cba4eb2cb 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9002,6 +9002,10 @@ class Sema final {
   void ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributes &InAttrs,
                              SmallVectorImpl<const Attr *> &OutAttrs);
 
+  ExprResult ActOnAssumeAttr(Stmt *St, const ParsedAttr &A, SourceRange Range);
+  ExprResult BuildAssumeExpr(Expr *Assumption, const IdentifierInfo *AttrName,
+                             SourceRange Range);
+
   ///@}
 
   //

>From 98ed8f3df96d4fc5419936288842310f1311ab14 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 6 Mar 2024 23:28:18 +0100
Subject: [PATCH 19/27] [NFC] Rename AssumeAttr -> CXXAssumeAttr,
 AssumptionAttr -> OMPAssumeAttr

---
 clang/include/clang/Basic/Attr.td             |  8 +--
 clang/include/clang/Basic/AttrDocs.td         |  8 +--
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +-
 clang/include/clang/Parse/Parser.h            |  8 +--
 clang/include/clang/Sema/Sema.h               | 12 ++--
 clang/lib/AST/ExprConstant.cpp                |  2 +-
 clang/lib/CodeGen/CGCall.cpp                  |  8 +--
 clang/lib/CodeGen/CGStmt.cpp                  |  4 +-
 clang/lib/Parse/ParseDeclCXX.cpp              | 10 +--
 clang/lib/Sema/SemaDeclAttr.cpp               | 19 +++---
 clang/lib/Sema/SemaExprCXX.cpp                | 50 ---------------
 clang/lib/Sema/SemaOpenMP.cpp                 |  6 +-
 clang/lib/Sema/SemaStmtAttr.cpp               | 64 +++++++++++++++++--
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 17 ++---
 14 files changed, 112 insertions(+), 108 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 0680bf69cee0f6..8d4ed38de4d8dc 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1572,11 +1572,11 @@ def Unlikely : StmtAttr {
 }
 def : MutualExclusions<[Likely, Unlikely]>;
 
-def Assume : StmtAttr {
+def CXXAssume : StmtAttr {
   let Spellings = [CXX11<"", "assume", 202302>];
   let Subjects = SubjectList<[NullStmt], ErrorDiag, "empty statements">;
   let Args = [ExprArgument<"Assumption">];
-  let Documentation = [AssumeDocs];
+  let Documentation = [CXXAssumeDocs];
 }
 
 def NoMerge : DeclOrStmtAttr {
@@ -4150,11 +4150,11 @@ def OMPDeclareVariant : InheritableAttr {
   }];
 }
 
-def Assumption : InheritableAttr {
+def OMPAssume : InheritableAttr {
   let Spellings = [Clang<"assume">];
   let Subjects = SubjectList<[Function, ObjCMethod]>;
   let InheritEvenIfAlreadyPresent = 1;
-  let Documentation = [AssumptionDocs];
+  let Documentation = [OMPAssumeDocs];
   let Args = [StringArgument<"Assumption">];
 }
 
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 31bb06d4441dca..11ba877149b0c5 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1996,12 +1996,12 @@ Here is an example:
   }];
 }
 
-def AssumeDocs : Documentation {
+def CXXAssumeDocs : Documentation {
   let Category = DocCatStmt;
   let Heading = "assume";
   let Content = [{
 The ``assume`` attribute is used to indicate to the optimizer that a
-certain condition can be assumed to be true at a certain point in the
+certain condition is assumed to be true at a certain point in the
 program. If this condition is violated at runtime, the behavior is
 undefined. ``assume`` can only be applied to a null statement.
 
@@ -2015,7 +2015,7 @@ Example:
   int f(int x, int y) {
     [[assume(x == 27)]];
     [[assume(x == y)]];
-    return y + 1; // Will be optimised to `return 28`.
+    return y + 1; // May be optimised to `return 28`.
   }
   }];
 }
@@ -4653,7 +4653,7 @@ For more information see
 }];
 }
 
-def AssumptionDocs : Documentation {
+def OMPAssumeDocs : Documentation {
   let Category = DocCatFunction;
   let Heading = "assume";
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5465af1eb712df..ad0425cc1b30a9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -855,10 +855,10 @@ def note_strncat_wrong_size : Note<
 def warn_assume_side_effects : Warning<
   "the argument to %0 has side effects that will be discarded">,
   InGroup<DiagGroup<"assume">>;
-def warn_assume_attribute_string_unknown : Warning<
+def warn_omp_assume_attribute_string_unknown : Warning<
   "unknown assumption string '%0'; attribute is potentially ignored">,
   InGroup<UnknownAssumption>;
-def warn_assume_attribute_string_unknown_suggested : Warning<
+def warn_omp_assume_attribute_string_unknown_suggested : Warning<
   "unknown assumption string '%0' may be misspelled; attribute is potentially "
   "ignored, did you mean '%1'?">,
   InGroup<MisspelledAssumption>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index f6536058983c6a..64e031d5094c74 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2957,10 +2957,10 @@ class Parser : public CodeCompletionHandler {
                                CachedTokens &OpenMPTokens);
 
   /// Parse a C++23 assume() attribute. Returns true on error.
-  bool ParseAssumeAttributeArg(ParsedAttributes &Attrs,
-                               IdentifierInfo *AttrName,
-                               SourceLocation AttrNameLoc,
-                               SourceLocation *EndLoc);
+  bool ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
+                                  IdentifierInfo *AttrName,
+                                  SourceLocation AttrNameLoc,
+                                  SourceLocation *EndLoc);
 
   IdentifierInfo *TryParseCXX11AttributeIdentifier(
       SourceLocation &Loc,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 27499cba4eb2cb..9fa57957a937f7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9002,9 +9002,11 @@ class Sema final {
   void ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributes &InAttrs,
                              SmallVectorImpl<const Attr *> &OutAttrs);
 
-  ExprResult ActOnAssumeAttr(Stmt *St, const ParsedAttr &A, SourceRange Range);
-  ExprResult BuildAssumeExpr(Expr *Assumption, const IdentifierInfo *AttrName,
-                             SourceRange Range);
+  ExprResult ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,
+                                SourceRange Range);
+  ExprResult BuildCXXAssumeExpr(Expr *Assumption,
+                                const IdentifierInfo *AttrName,
+                                SourceRange Range);
 
   ///@}
 
@@ -14697,10 +14699,10 @@ class Sema final {
   SmallVector<OMPDeclareVariantScope, 4> OMPDeclareVariantScopes;
 
   /// The current `omp begin/end assumes` scopes.
-  SmallVector<AssumptionAttr *, 4> OMPAssumeScoped;
+  SmallVector<OMPAssumeAttr *, 4> OMPAssumeScoped;
 
   /// All `omp assumes` we encountered so far.
-  SmallVector<AssumptionAttr *, 4> OMPAssumeGlobal;
+  SmallVector<OMPAssumeAttr *, 4> OMPAssumeGlobal;
 
   /// OMPD_loop is mapped to OMPD_for, OMPD_distribute or OMPD_simd depending
   /// on the parameter of the bind clause. In the methods for the
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 93475ca24a8b94..b80d4da26b6a68 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5584,7 +5584,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
                                isa<ReturnStmt>(SS));
 
     for (auto *Attr : AS->getAttrs()) {
-      auto *AA = dyn_cast<AssumeAttr>(Attr);
+      auto *AA = dyn_cast<CXXAssumeAttr>(Attr);
       if (!AA)
         continue;
 
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 13f68237b464d6..a28d7888715d85 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -1796,14 +1796,14 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx,
     FuncAttrs.addAttribute("aarch64_inout_zt0");
 }
 
-static void AddAttributesFromAssumes(llvm::AttrBuilder &FuncAttrs,
-                                     const Decl *Callee) {
+static void AddAttributesFromOMPAssumes(llvm::AttrBuilder &FuncAttrs,
+                                        const Decl *Callee) {
   if (!Callee)
     return;
 
   SmallVector<StringRef, 4> Attrs;
 
-  for (const AssumptionAttr *AA : Callee->specific_attrs<AssumptionAttr>())
+  for (const OMPAssumeAttr *AA : Callee->specific_attrs<OMPAssumeAttr>())
     AA->getAssumption().split(Attrs, ",");
 
   if (!Attrs.empty())
@@ -2344,7 +2344,7 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
 
   // Attach assumption attributes to the declaration. If this is a call
   // site, attach assumptions from the caller to the call as well.
-  AddAttributesFromAssumes(FuncAttrs, TargetDecl);
+  AddAttributesFromOMPAssumes(FuncAttrs, TargetDecl);
 
   bool HasOptnone = false;
   // The NoBuiltinAttr attached to the target FunctionDecl.
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 9ac703fc9ce40f..d6f9a0d7ea7323 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -733,8 +733,8 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
       const ReturnStmt *R = cast<ReturnStmt>(Sub);
       musttail = cast<CallExpr>(R->getRetValue()->IgnoreParens());
     } break;
-    case attr::Assume: {
-      const Expr *Assumption = cast<AssumeAttr>(A)->getAssumption();
+    case attr::CXXAssume: {
+      const Expr *Assumption = cast<CXXAssumeAttr>(A)->getAssumption();
       if (!Assumption->HasSideEffects(getContext())) {
         llvm::Value *AssumptionVal = EvaluateExprAsBool(Assumption);
         Builder.CreateAssumption(AssumptionVal);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 5b68eb713b1b41..b6269b76a398c1 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4529,10 +4529,10 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
 }
 
 /// Parse the argument to C++23's [[assume()]] attribute.
-bool Parser::ParseAssumeAttributeArg(ParsedAttributes &Attrs,
-                                     IdentifierInfo *AttrName,
-                                     SourceLocation AttrNameLoc,
-                                     SourceLocation *EndLoc) {
+bool Parser::ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
+                                        IdentifierInfo *AttrName,
+                                        SourceLocation AttrNameLoc,
+                                        SourceLocation *EndLoc) {
   assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
   BalancedDelimiterTracker T(*this, tok::l_paren);
   T.consumeOpen();
@@ -4646,7 +4646,7 @@ bool Parser::ParseCXX11AttributeArgs(
                                       ScopeName, ScopeLoc, Form);
   // So does C++23's assume() attribute.
   else if (!ScopeName && AttrName->isStr("assume")) {
-    if (ParseAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc))
+    if (ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc))
       return true;
     NumArgs = 1;
   } else
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 397b5db0dc0669..e029782b5124be 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1771,8 +1771,8 @@ void Sema::AddAllocAlignAttr(Decl *D, const AttributeCommonInfo &CI,
 }
 
 /// Check if \p AssumptionStr is a known assumption and warn if not.
-static void checkAssumptionAttr(Sema &S, SourceLocation Loc,
-                                StringRef AssumptionStr) {
+static void checkOMPAssumeAttr(Sema &S, SourceLocation Loc,
+                               StringRef AssumptionStr) {
   if (llvm::KnownAssumptionStrings.count(AssumptionStr))
     return;
 
@@ -1788,22 +1788,23 @@ static void checkAssumptionAttr(Sema &S, SourceLocation Loc,
   }
 
   if (!Suggestion.empty())
-    S.Diag(Loc, diag::warn_assume_attribute_string_unknown_suggested)
+    S.Diag(Loc, diag::warn_omp_assume_attribute_string_unknown_suggested)
         << AssumptionStr << Suggestion;
   else
-    S.Diag(Loc, diag::warn_assume_attribute_string_unknown) << AssumptionStr;
+    S.Diag(Loc, diag::warn_omp_assume_attribute_string_unknown)
+        << AssumptionStr;
 }
 
-static void handleAssumumptionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+static void handleOMPAssumeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   // Handle the case where the attribute has a text message.
   StringRef Str;
   SourceLocation AttrStrLoc;
   if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &AttrStrLoc))
     return;
 
-  checkAssumptionAttr(S, AttrStrLoc, Str);
+  checkOMPAssumeAttr(S, AttrStrLoc, Str);
 
-  D->addAttr(::new (S.Context) AssumptionAttr(S.Context, AL, Str));
+  D->addAttr(::new (S.Context) OMPAssumeAttr(S.Context, AL, Str));
 }
 
 /// Normalize the attribute, __foo__ becomes foo.
@@ -9489,8 +9490,8 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
   case ParsedAttr::AT_Unavailable:
     handleAttrWithMessage<UnavailableAttr>(S, D, AL);
     break;
-  case ParsedAttr::AT_Assumption:
-    handleAssumumptionAttr(S, D, AL);
+  case ParsedAttr::AT_OMPAssume:
+    handleOMPAssumeAttr(S, D, AL);
     break;
   case ParsedAttr::AT_ObjCDirect:
     handleObjCDirectAttr(S, D, AL);
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 8c84dd7273fc52..c34a40fa7c81ac 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -9335,53 +9335,3 @@ ExprResult Sema::ActOnRequiresExpr(
     return ExprError();
   return RE;
 }
-
-ExprResult Sema::ActOnAssumeAttr(Stmt *St, const ParsedAttr &A,
-                                 SourceRange Range) {
-  if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {
-    Diag(A.getLoc(), diag::err_assume_attr_args) << A.getAttrName() << Range;
-    return ExprError();
-  }
-
-  if (!isa<NullStmt>(St)) {
-    Diag(A.getLoc(), diag::err_assume_attr_wrong_target)
-        << A.getAttrName() << Range;
-    return ExprError();
-  }
-
-  auto *Assumption = A.getArgAsExpr(0);
-  if (Assumption->getDependence() == ExprDependence::None) {
-    ExprResult Res = BuildAssumeExpr(Assumption, A.getAttrName(), Range);
-    if (Res.isInvalid())
-      return ExprError();
-    Assumption = Res.get();
-  }
-
-  if (!getLangOpts().CPlusPlus23)
-    Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;
-
-  return Assumption;
-}
-
-ExprResult Sema::BuildAssumeExpr(Expr *Assumption,
-                                 const IdentifierInfo *AttrName,
-                                 SourceRange Range) {
-  ExprResult Res = CorrectDelayedTyposInExpr(Assumption);
-  if (Res.isInvalid())
-    return ExprError();
-
-  Res = CheckPlaceholderExpr(Res.get());
-  if (Res.isInvalid())
-    return ExprError();
-
-  Res = PerformContextuallyConvertToBool(Res.get());
-  if (Res.isInvalid())
-    return ExprError();
-
-  Assumption = Res.get();
-  if (Assumption->HasSideEffects(Context))
-    Diag(Assumption->getBeginLoc(), diag::warn_assume_side_effects)
-        << AttrName << Range;
-
-  return Assumption;
-}
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index f4364a259ad57f..f143b2f4f75473 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3496,7 +3496,7 @@ void Sema::ActOnOpenMPAssumesDirective(SourceLocation Loc,
         << llvm::omp::getAllAssumeClauseOptions()
         << llvm::omp::getOpenMPDirectiveName(DKind);
 
-  auto *AA = AssumptionAttr::Create(Context, llvm::join(Assumptions, ","), Loc);
+  auto *AA = OMPAssumeAttr::Create(Context, llvm::join(Assumptions, ","), Loc);
   if (DKind == llvm::omp::Directive::OMPD_begin_assumes) {
     OMPAssumeScoped.push_back(AA);
     return;
@@ -7275,10 +7275,10 @@ void Sema::ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Decl *D) {
   // only global ones. We apply scoped assumption to the template definition
   // though.
   if (!inTemplateInstantiation()) {
-    for (AssumptionAttr *AA : OMPAssumeScoped)
+    for (OMPAssumeAttr *AA : OMPAssumeScoped)
       FD->addAttr(AA);
   }
-  for (AssumptionAttr *AA : OMPAssumeGlobal)
+  for (OMPAssumeAttr *AA : OMPAssumeGlobal)
     FD->addAttr(AA);
 }
 
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 4ebb016804f9d8..1fea8753ff5eff 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -303,13 +303,13 @@ static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
   return ::new (S.Context) AlwaysInlineAttr(S.Context, A);
 }
 
-Attr *handleAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
-                       SourceRange Range) {
-  ExprResult Res = S.ActOnAssumeAttr(St, A, Range);
-  if (Res.isInvalid())
+static Attr *handleCXXAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
+                                 SourceRange Range) {
+  ExprResult Res = S.ActOnCXXAssumeAttr(St, A, Range);
+  if (!Res.isUsable())
     return nullptr;
 
-  return ::new (S.Context) AssumeAttr(S.Context, A, Res.get());
+  return ::new (S.Context) CXXAssumeAttr(S.Context, A, Res.get());
 }
 
 static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,
@@ -603,8 +603,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
   switch (A.getKind()) {
   case ParsedAttr::AT_AlwaysInline:
     return handleAlwaysInlineAttr(S, St, A, Range);
-  case ParsedAttr::AT_Assume:
-    return handleAssumeAttr(S, St, A, Range);
+  case ParsedAttr::AT_CXXAssume:
+    return handleCXXAssumeAttr(S, St, A, Range);
   case ParsedAttr::AT_FallThrough:
     return handleFallThroughAttr(S, St, A, Range);
   case ParsedAttr::AT_LoopHint:
@@ -652,3 +652,53 @@ bool Sema::CheckRebuiltStmtAttributes(ArrayRef<const Attr *> Attrs) {
   CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, Attrs);
   return false;
 }
+
+ExprResult Sema::ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,
+                                    SourceRange Range) {
+  if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {
+    Diag(A.getLoc(), diag::err_assume_attr_args) << A.getAttrName() << Range;
+    return ExprError();
+  }
+
+  if (!isa<NullStmt>(St)) {
+    Diag(A.getLoc(), diag::err_assume_attr_wrong_target)
+        << A.getAttrName() << Range;
+    return ExprError();
+  }
+
+  auto *Assumption = A.getArgAsExpr(0);
+  if (Assumption->getDependence() == ExprDependence::None) {
+    ExprResult Res = BuildCXXAssumeExpr(Assumption, A.getAttrName(), Range);
+    if (Res.isInvalid())
+      return ExprError();
+    Assumption = Res.get();
+  }
+
+  if (!getLangOpts().CPlusPlus23)
+    Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;
+
+  return Assumption;
+}
+
+ExprResult Sema::BuildCXXAssumeExpr(Expr *Assumption,
+                                    const IdentifierInfo *AttrName,
+                                    SourceRange Range) {
+  ExprResult Res = CorrectDelayedTyposInExpr(Assumption);
+  if (Res.isInvalid())
+    return ExprError();
+
+  Res = CheckPlaceholderExpr(Res.get());
+  if (Res.isInvalid())
+    return ExprError();
+
+  Res = PerformContextuallyConvertToBool(Res.get());
+  if (Res.isInvalid())
+    return ExprError();
+
+  Assumption = Res.get();
+  if (Assumption->HasSideEffects(Context))
+    Diag(Assumption->getBeginLoc(), diag::warn_assume_side_effects)
+        << AttrName << Range;
+
+  return Assumption;
+}
\ No newline at end of file
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 935e26028e1dce..e853221a7fcf60 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1410,7 +1410,7 @@ namespace {
                           NamedDecl *FirstQualifierInScope = nullptr,
                           bool AllowInjectedClassName = false);
 
-    const AssumeAttr *TransformAssumeAttr(const AssumeAttr *AA);
+    const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
     const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
     const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS,
                                                   const Stmt *InstS,
@@ -1927,18 +1927,19 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
                                          Arg, PackIndex);
 }
 
-const AssumeAttr *
-TemplateInstantiator::TransformAssumeAttr(const AssumeAttr *AA) {
+const CXXAssumeAttr *
+TemplateInstantiator::TransformCXXAssumeAttr(const CXXAssumeAttr *AA) {
   ExprResult Res = getDerived().TransformExpr(AA->getAssumption());
-  if (Res.isInvalid())
+  if (!Res.isUsable())
     return AA;
 
-  Res = getSema().BuildAssumeExpr(Res.get(), AA->getAttrName(), AA->getRange());
-  if (Res.isInvalid())
+  Res = getSema().BuildCXXAssumeExpr(Res.get(), AA->getAttrName(),
+                                     AA->getRange());
+  if (!Res.isUsable())
     return AA;
 
-  return AssumeAttr::CreateImplicit(getSema().Context, Res.get(),
-                                    AA->getRange());
+  return CXXAssumeAttr::CreateImplicit(getSema().Context, Res.get(),
+                                       AA->getRange());
 }
 
 const LoopHintAttr *

>From 6b30e99e973592090b33d928458cb8a4db77ceb0 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 6 Mar 2024 23:30:01 +0100
Subject: [PATCH 20/27] [Clang] Ignore failed assumptions in constant
 expressions in MSVCCompat mode

---
 clang/lib/AST/ExprConstant.cpp | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b80d4da26b6a68..1303d5a01fad36 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5583,22 +5583,24 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
         *Info.CurrentCall, hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
                                isa<ReturnStmt>(SS));
 
-    for (auto *Attr : AS->getAttrs()) {
-      auto *AA = dyn_cast<CXXAssumeAttr>(Attr);
-      if (!AA)
-        continue;
+    if (!Info.getCtx().getLangOpts().MSVCCompat) {
+      for (auto *Attr : AS->getAttrs()) {
+        auto *AA = dyn_cast<CXXAssumeAttr>(Attr);
+        if (!AA)
+          continue;
 
-      auto *Assumption = AA->getAssumption();
-      if (Assumption->isValueDependent())
-        return ESR_Failed;
+        auto *Assumption = AA->getAssumption();
+        if (Assumption->isValueDependent())
+          return ESR_Failed;
 
-      bool Value;
-      if (!EvaluateAsBooleanCondition(Assumption, Value, Info))
-        return ESR_Failed;
-      if (!Value) {
-        Info.CCEDiag(Assumption->getExprLoc(),
-                     diag::note_constexpr_assumption_failed);
-        return ESR_Failed;
+        bool Value;
+        if (!EvaluateAsBooleanCondition(Assumption, Value, Info))
+          return ESR_Failed;
+        if (!Value) {
+          Info.CCEDiag(Assumption->getExprLoc(),
+                       diag::note_constexpr_assumption_failed);
+          return ESR_Failed;
+        }
       }
     }
 

>From c41566b05f9d495f1d1ca4db0b3396b67bcb6d84 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 7 Mar 2024 01:16:38 +0100
Subject: [PATCH 21/27] [Clang] Add assume lang option and command-line flag

---
 clang/include/clang/Basic/LangOptions.def    |  2 ++
 clang/include/clang/Driver/Options.td        |  6 ++++++
 clang/lib/AST/ExprConstant.cpp               |  3 ++-
 clang/lib/CodeGen/CGStmt.cpp                 |  3 ++-
 clang/lib/Driver/ToolChains/Clang.cpp        |  5 +++++
 clang/test/CodeGenCXX/cxx23-assume.cpp       |  3 +++
 clang/test/SemaCXX/cxx23-assume-disabled.cpp | 14 ++++++++++++++
 7 files changed, 34 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaCXX/cxx23-assume-disabled.cpp

diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 2b42b521a30363..472fd9f093a718 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -450,6 +450,8 @@ LANGOPT(RegCall4, 1, 0, "Set __regcall4 as a default calling convention to respe
 
 LANGOPT(MatrixTypes, 1, 0, "Enable or disable the builtin matrix type")
 
+LANGOPT(CXXAssumptions, 1, 1, "Enable or disable codegen and compile-time checks for C++23's [[assume]] attribute")
+
 ENUM_LANGOPT(StrictFlexArraysLevel, StrictFlexArraysLevelKind, 2,
              StrictFlexArraysLevelKind::Default,
              "Rely on strict definition of flexible arrays")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index bef38738fde82e..18c587563e8f06 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3789,6 +3789,12 @@ def foptimization_record_passes_EQ : Joined<["-"], "foptimization-record-passes=
   HelpText<"Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)">,
   MetaVarName<"<regex>">;
 
+defm assumptions : BoolFOption<"assumptions",
+  LangOpts<"CXXAssumptions">, DefaultTrue,
+  NegFlag<SetFalse, [], [ClangOption, CC1Option],
+          "Disable codegen and compile-time checks for C++23's [[assume]] attribute">,
+  PosFlag<SetTrue>>;
+
 def fvectorize : Flag<["-"], "fvectorize">, Group<f_Group>,
   HelpText<"Enable the loop vectorization passes">;
 def fno_vectorize : Flag<["-"], "fno-vectorize">, Group<f_Group>;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 1303d5a01fad36..f567866b7b821f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5583,7 +5583,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
         *Info.CurrentCall, hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
                                isa<ReturnStmt>(SS));
 
-    if (!Info.getCtx().getLangOpts().MSVCCompat) {
+    auto LO = Info.getCtx().getLangOpts();
+    if (LO.CXXAssumptions && !LO.MSVCCompat) {
       for (auto *Attr : AS->getAttrs()) {
         auto *AA = dyn_cast<CXXAssumeAttr>(Attr);
         if (!AA)
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index d6f9a0d7ea7323..8898e3f22a7df6 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -735,7 +735,8 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
     } break;
     case attr::CXXAssume: {
       const Expr *Assumption = cast<CXXAssumeAttr>(A)->getAssumption();
-      if (!Assumption->HasSideEffects(getContext())) {
+      if (getLangOpts().CXXAssumptions &&
+          !Assumption->HasSideEffects(getContext())) {
         llvm::Value *AssumptionVal = EvaluateExprAsBool(Assumption);
         Builder.CreateAssumption(AssumptionVal);
       }
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 858d20fbfac015..58f3e71e59d9b5 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6976,6 +6976,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
                         (!IsWindowsMSVC || IsMSVC2015Compatible)))
     CmdArgs.push_back("-fno-threadsafe-statics");
 
+  // Add -fno-assumptions, if it was specified.
+  if (!Args.hasFlag(options::OPT_fassumptions, options::OPT_fno_assumptions,
+                    true))
+    CmdArgs.push_back("-fno-assumptions");
+
   // -fgnu-keywords default varies depending on language; only pass if
   // specified.
   Args.AddLastArg(CmdArgs, options::OPT_fgnu_keywords,
diff --git a/clang/test/CodeGenCXX/cxx23-assume.cpp b/clang/test/CodeGenCXX/cxx23-assume.cpp
index 3e8fc522563ba9..a1fa6b30b2f0e1 100644
--- a/clang/test/CodeGenCXX/cxx23-assume.cpp
+++ b/clang/test/CodeGenCXX/cxx23-assume.cpp
@@ -1,4 +1,7 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 -fno-assumptions %s -emit-llvm -o - | FileCheck %s --check-prefix=DISABLED
+
+// DISABLED-NOT: @llvm.assume
 
 bool f();
 
diff --git a/clang/test/SemaCXX/cxx23-assume-disabled.cpp b/clang/test/SemaCXX/cxx23-assume-disabled.cpp
new file mode 100644
index 00000000000000..4233a2f7f43384
--- /dev/null
+++ b/clang/test/SemaCXX/cxx23-assume-disabled.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -std=c++23 -x c++ %s -fno-assumptions -verify
+// RUN: %clang_cc1 -std=c++23 -x c++ %s -fms-compatibility -verify
+// expected-no-diagnostics
+
+// We don't check assumptions at compile time if '-fno-assumptions' is passed,
+// or if we're in MSVCCompat mode
+
+constexpr bool f(bool x) {
+  [[assume(x)]];
+  return true;
+}
+
+static_assert(f(false));
+

>From d85d46bd7e45e104d5a910251506008f21f2425c Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 7 Mar 2024 01:27:20 +0100
Subject: [PATCH 22/27] [Clang] Ensure __has_cpp_attribute(assume) returns the
 correct value

---
 clang/include/clang/Basic/Attr.td   | 2 +-
 clang/test/SemaCXX/cxx23-assume.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8d4ed38de4d8dc..0be7bc550842c0 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1573,7 +1573,7 @@ def Unlikely : StmtAttr {
 def : MutualExclusions<[Likely, Unlikely]>;
 
 def CXXAssume : StmtAttr {
-  let Spellings = [CXX11<"", "assume", 202302>];
+  let Spellings = [CXX11<"", "assume", 202207>];
   let Subjects = SubjectList<[NullStmt], ErrorDiag, "empty statements">;
   let Args = [ExprArgument<"Assumption">];
   let Documentation = [CXXAssumeDocs];
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index d50cea0a469bc8..2b99cbd3e788a1 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -62,7 +62,7 @@ void g(int x) {
 constexpr int h(int x) { return sizeof([=] { [[assume(x)]]; }); } // ext-warning {{C++23 extension}}
 static_assert(h(4) == sizeof(int));
 
-static_assert(__has_cpp_attribute(assume));
+static_assert(__has_cpp_attribute(assume) == 202207L);
 static_assert(__has_attribute(assume));
 
 constexpr bool i() { // expected-error {{never produces a constant expression}}

>From 33bbd8dfc284ab4ee39edf9e770a96297e728dbc Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 7 Mar 2024 16:07:24 +0100
Subject: [PATCH 23/27] [NFC] Add newline at end of file

---
 clang/lib/Sema/SemaStmtAttr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 1fea8753ff5eff..dd8ed3545234ed 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -701,4 +701,4 @@ ExprResult Sema::BuildCXXAssumeExpr(Expr *Assumption,
         << AttrName << Range;
 
   return Assumption;
-}
\ No newline at end of file
+}

>From 7aa1a9504c2587d927961b60e3e79d3e6d40d7e0 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 7 Mar 2024 16:08:51 +0100
Subject: [PATCH 24/27] [Clang] Update cxx_status

---
 clang/www/cxx_status.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 301f141b2f2b60..4c9ddbb94bb1da 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -388,7 +388,7 @@ <h2 id="cxx23">C++23 implementation status</h2>
     <tr>
       <td>Portable assumptions</td>
       <td><a href="https://wg21.link/P1774R8">P1774R8</a></td>
-      <td class="none" align="center">No</td>
+      <td class="full" align="center">Clang 19</td>
     </tr>
     <tr>
       <td>Support for UTF-8 as a portable source file encoding</td>

>From 61230cbde962d3d79a4598675f82afa42733d5b0 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 7 Mar 2024 16:14:17 +0100
Subject: [PATCH 25/27] [Clang] Rename last occurrence of Assumption ->
 OMPAssume

---
 clang/test/Misc/pragma-attribute-supported-attributes-list.test | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 1528388e3298eb..ec84ebdc6abe7b 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -19,7 +19,6 @@
 // CHECK-NEXT: ArcWeakrefUnavailable (SubjectMatchRule_objc_interface)
 // CHECK-NEXT: ArmBuiltinAlias (SubjectMatchRule_function)
 // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function)
-// CHECK-NEXT: Assumption (SubjectMatchRule_function, SubjectMatchRule_objc_method)
 // CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
 // CHECK-NEXT: AvailableOnlyInDefaultEvalMethod (SubjectMatchRule_type_alias)
 // CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record)
@@ -127,6 +126,7 @@
 // CHECK-NEXT: NoThrow (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: NoUwtable (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: NotTailCalled (SubjectMatchRule_function)
+// CHECK-NEXT: OMPAssume (SubjectMatchRule_function, SubjectMatchRule_objc_method)
 // CHECK-NEXT: OSConsumed (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: OSReturnsNotRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: OSReturnsRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_variable_is_parameter)

>From df227c423510de17fbd488bfca092d0e67e6f2e5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 7 Mar 2024 17:23:13 +0100
Subject: [PATCH 26/27] [Clang] [Parser] Better diagnostics for [[assume]]
 syntax errors

---
 .../clang/Basic/DiagnosticParseKinds.td        |  2 +-
 .../include/clang/Basic/DiagnosticSemaKinds.td |  2 --
 clang/lib/Parse/ParseDeclCXX.cpp               | 18 ++++++++++++++++--
 clang/lib/Sema/SemaStmtAttr.cpp                |  6 ------
 clang/test/Parser/cxx23-assume.cpp             |  6 +++++-
 5 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 28295280baa346..816c3ff5f8b2aa 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -787,7 +787,7 @@ def err_ms_property_initializer : Error<
   "property declaration cannot have a default member initializer">;
 
 def err_assume_attr_expects_cond_expr : Error<
-  "use of this expression in an '%0' attribute requires parentheses">;
+  "use of this expression in an %0 attribute requires parentheses">;
 
 def warn_cxx20_compat_explicit_bool : Warning<
   "this expression will be parsed as explicit(bool) in C++20">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ad0425cc1b30a9..757c90c1a8a05b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10177,8 +10177,6 @@ def err_fallthrough_attr_invalid_placement : Error<
 
 def err_assume_attr_args : Error<
   "attribute '%0' requires a single expression argument">;
-def err_assume_attr_wrong_target : Error<
-  "'%0' attribute is only allowed on empty statements">;
 
 def warn_unreachable_default : Warning<
   "default label in switch which covers all enumeration values">,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index b6269b76a398c1..a7c2f54f644609 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4537,6 +4537,13 @@ bool Parser::ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
   BalancedDelimiterTracker T(*this, tok::l_paren);
   T.consumeOpen();
 
+  // Handle the '()' case for a better diagnostic.
+  if (Tok.is(tok::r_paren)) {
+    Diag(Tok.getLocation(), diag::err_expected_expression);
+    T.consumeClose();
+    return true;
+  }
+
   // [dcl.attr.assume]: The expression is potentially evaluated.
   EnterExpressionEvaluationContext Unevaluated(
       Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
@@ -4544,11 +4551,18 @@ bool Parser::ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
   TentativeParsingAction TPA(*this);
   ExprResult Res(
       Actions.CorrectDelayedTyposInExpr(ParseConditionalExpression()));
-  if (Res.isInvalid() || !Tok.is(tok::r_paren)) {
+  if (Res.isInvalid()) {
+    TPA.Commit();
+    SkipUntil(tok::r_paren, tok::r_square, StopAtSemi | StopBeforeMatch);
+    if (Tok.is(tok::r_paren))
+      T.consumeClose();
+    return true;
+  }
+
+  if (!Tok.isOneOf(tok::r_paren, tok::r_square)) {
     // Emit a better diagnostic if this is an otherwise valid expression that
     // is not allowed here.
     TPA.Revert();
-    Sema::TentativeAnalysisScope Scope(Actions);
     Res = ParseExpression();
     if (!Res.isInvalid()) {
       auto *E = Res.get();
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index dd8ed3545234ed..691857e88beb49 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -660,12 +660,6 @@ ExprResult Sema::ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,
     return ExprError();
   }
 
-  if (!isa<NullStmt>(St)) {
-    Diag(A.getLoc(), diag::err_assume_attr_wrong_target)
-        << A.getAttrName() << Range;
-    return ExprError();
-  }
-
   auto *Assumption = A.getArgAsExpr(0);
   if (Assumption->getDependence() == ExprDependence::None) {
     ExprResult Res = BuildCXXAssumeExpr(Assumption, A.getAttrName(), Range);
diff --git a/clang/test/Parser/cxx23-assume.cpp b/clang/test/Parser/cxx23-assume.cpp
index 2a9b8b6a248821..269fb7e599443e 100644
--- a/clang/test/Parser/cxx23-assume.cpp
+++ b/clang/test/Parser/cxx23-assume.cpp
@@ -9,6 +9,10 @@ void f(int x, int y) {
   [[assume(x && y)]];
   [[assume(true)]] [[assume(true)]];
 
+  [[assume]]; // expected-error {{takes one argument}}
+  [[assume(]]; // expected-error {{expected expression}}
+  [[assume()]]; // expected-error {{expected expression}}
+  [[assume(2]]; // expected-error {{expected ')'}} expected-note {{to match this '('}}
   [[assume(x = 2)]]; // expected-error {{requires parentheses}}
-  [[assume(2, 3)]]; // expected-error {{requires parentheses}}
+  [[assume(2, 3)]]; // expected-error {{requires parentheses}} expected-warning {{has no effect}}
 }

>From 0d205809aad53c74329bd7cc7f231ad015bea02e Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 7 Mar 2024 17:25:19 +0100
Subject: [PATCH 27/27] [Clang] [Parser] Remove superfluous check for ()

---
 clang/lib/Parse/ParseDeclCXX.cpp | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index a7c2f54f644609..bdca10c4c7c0b4 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4537,13 +4537,6 @@ bool Parser::ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
   BalancedDelimiterTracker T(*this, tok::l_paren);
   T.consumeOpen();
 
-  // Handle the '()' case for a better diagnostic.
-  if (Tok.is(tok::r_paren)) {
-    Diag(Tok.getLocation(), diag::err_expected_expression);
-    T.consumeClose();
-    return true;
-  }
-
   // [dcl.attr.assume]: The expression is potentially evaluated.
   EnterExpressionEvaluationContext Unevaluated(
       Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);



More information about the cfe-commits mailing list