https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/81014
>From 5ba669011752b6ea6e6fe5f6141c4af66ca7ccbf Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 17:25:10 +0100 Subject: [PATCH 01/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 17:42:30 +0100 Subject: [PATCH 02/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:01:36 +0100 Subject: [PATCH 03/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:11:10 +0100 Subject: [PATCH 04/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:14:21 +0100 Subject: [PATCH 05/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:18:47 +0100 Subject: [PATCH 06/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:31:04 +0100 Subject: [PATCH 07/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:37:43 +0100 Subject: [PATCH 08/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:40:40 +0100 Subject: [PATCH 09/10] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 19:43:58 +0100 Subject: [PATCH 10/10] [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; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits