[clang] [Clang][C++23] Implement P1774R8: Portable assumptions (PR #81014)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 7 20:58:40 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/15] [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 b2d5309e142c1..2804ff1aea005 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 6765721ae7002..192b081404a82 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 a30ab27566ec3..9ecfdab3617e0 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 b4dc4feee8e63..847168af28862 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 da18cf88edcc9..0f982dbb67b41 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 79928ddb5af59..768aca68030fe 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 52cebdb6f64ba..cf2a7bd026c5d 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 e6a4d3e63e4aa..41f6461e183e7 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 0000000000000..ecf6754600ccb
--- /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/15] [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 768aca68030fe..7f2762d4fe22b 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 ecf6754600ccb..5f14a35144554 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 0000000000000..9fdcda31947e7
--- /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/15] [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 41f6461e183e7..e712745a237c3 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 5f14a35144554..968caae7dab9a 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 9fdcda31947e7..d3132c55f4b47 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/15] [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 beff0ad9da270..66be305550b1b 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 0000000000000..8a8438e8edc05
--- /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/15] [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 d3132c55f4b47..15ea831f77f10 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/15] [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 968caae7dab9a..2a9b8b6a24882 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/15] [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 802c44b6c8608..6312e10091361 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 2804ff1aea005..6cd2a541da17d 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 041786f37fb8a..1b27a46ef1d90 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/15] [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 6cd2a541da17d..d5810221b633b 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/15] [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 e712745a237c3..0812bc8761c9a 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/15] [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 9ecfdab3617e0..351c9eeca2de4 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 847168af28862..b9f0d1fc81e0d 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 0812bc8761c9a..5f22fcd4ff0ac 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/15] [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 c81d17ed64108..a024f9b2a9f8c 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 089bc2094567f..7a61273fccc7a 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 15ea831f77f10..6e8fe812e9df2 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/15] [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 7a61273fccc7a..716de07f2b770 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/15] [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 3c26003b5bda7..88303bc6809d4 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 716de07f2b770..67053d08e7d49 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 246d2313e089f..5d888036cafd1 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 5f22fcd4ff0ac..4ebb016804f9d 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 6d59180bc446d..9bd6b2f494e93 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 6e8fe812e9df2..4c6e34c267655 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/15] [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 4c6e34c267655..d50cea0a469bc 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/15] [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 0000000000000..1e858ad095616
--- /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
More information about the cfe-commits
mailing list