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/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 17:42:30 +0100 Subject: [PATCH 02/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:01:36 +0100 Subject: [PATCH 03/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:11:10 +0100 Subject: [PATCH 04/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:14:21 +0100 Subject: [PATCH 05/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:18:47 +0100 Subject: [PATCH 06/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:31:04 +0100 Subject: [PATCH 07/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:37:43 +0100 Subject: [PATCH 08/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 18:40:40 +0100 Subject: [PATCH 09/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 19:43:58 +0100 Subject: [PATCH 10/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 20:12:28 +0100 Subject: [PATCH 11/26] [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 <aeternalm...@gmail.com> Date: Wed, 7 Feb 2024 20:13:25 +0100 Subject: [PATCH 12/26] [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 <aeternalm...@gmail.com> Date: Thu, 8 Feb 2024 05:05:20 +0100 Subject: [PATCH 13/26] [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 <aeternalm...@gmail.com> Date: Thu, 8 Feb 2024 05:51:28 +0100 Subject: [PATCH 14/26] [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@-1 3 {{while substituting}} + // expected-error@-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@-1 {{while substituting template arguments}} + // expected-note@-2 {{candidate template ignored}} + +template <typename T> +constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}} + // expected-note@-1 4 {{while substituting template arguments}} + // expected-note@-2 {{candidate template ignored}} + +static_assert(f5<int>() == 1); +static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}} + // expected-note@-1 3 {{in instantiation of}} + // expected-error@-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 <aeternalm...@gmail.com> Date: Thu, 8 Feb 2024 05:58:22 +0100 Subject: [PATCH 15/26] [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 <aeternalm...@gmail.com> Date: Thu, 8 Feb 2024 05:59:49 +0100 Subject: [PATCH 16/26] [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 <aeternalm...@gmail.com> Date: Thu, 8 Feb 2024 06:10:10 +0100 Subject: [PATCH 17/26] [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 <aeternalm...@gmail.com> Date: Wed, 6 Mar 2024 22:57:40 +0100 Subject: [PATCH 18/26] [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 <aeternalm...@gmail.com> Date: Wed, 6 Mar 2024 23:28:18 +0100 Subject: [PATCH 19/26] [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 <aeternalm...@gmail.com> Date: Wed, 6 Mar 2024 23:30:01 +0100 Subject: [PATCH 20/26] [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 <aeternalm...@gmail.com> Date: Thu, 7 Mar 2024 01:16:38 +0100 Subject: [PATCH 21/26] [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 <aeternalm...@gmail.com> Date: Thu, 7 Mar 2024 01:27:20 +0100 Subject: [PATCH 22/26] [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 <aeternalm...@gmail.com> Date: Thu, 7 Mar 2024 16:07:24 +0100 Subject: [PATCH 23/26] [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 <aeternalm...@gmail.com> Date: Thu, 7 Mar 2024 16:08:51 +0100 Subject: [PATCH 24/26] [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 <aeternalm...@gmail.com> Date: Thu, 7 Mar 2024 16:14:17 +0100 Subject: [PATCH 25/26] [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 <aeternalm...@gmail.com> Date: Thu, 7 Mar 2024 17:23:13 +0100 Subject: [PATCH 26/26] [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}} } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits