RIscRIpt updated this revision to Diff 538026. RIscRIpt added a comment. Rebased onto main
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D134475/new/ https://reviews.llvm.org/D134475 Files: clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticASTKinds.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/AST/ExprConstant.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclAttr.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaStmtAttr.cpp clang/test/AST/msvc-attrs-invalid.cpp clang/test/AST/msvc-attrs.cpp clang/test/AST/msvc-constexpr-new.cpp clang/test/Misc/pragma-attribute-supported-attributes-list.test
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -84,6 +84,7 @@ // CHECK-NEXT: LoaderUninitialized (SubjectMatchRule_variable_is_global) // CHECK-NEXT: Lockable (SubjectMatchRule_record) // CHECK-NEXT: MIGServerRoutine (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_block) +// CHECK-NEXT: MSConstexpr (SubjectMatchRule_function) // CHECK-NEXT: MSStruct (SubjectMatchRule_record) // CHECK-NEXT: MaybeUndef (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: MicroMips (SubjectMatchRule_function) Index: clang/test/AST/msvc-constexpr-new.cpp =================================================================== --- /dev/null +++ clang/test/AST/msvc-constexpr-new.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fms-extensions -std=c++20 -ast-dump %s | FileCheck %s + +// CHECK: used operator new +// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:17, col:23> +[[nodiscard]] [[msvc::constexpr]] inline void* __cdecl operator new(decltype(sizeof(void*)), void* p) noexcept { return p; } + +// CHECK: used constexpr construct_at +// CHECK: AttributedStmt 0x{{[0-9a-f]+}} <col:46, col:88> +// CHECK-NEXT: MSConstexprAttr 0x{{[0-9a-f]+}} <col:48, col:54> +// CHECK-NEXT: ReturnStmt 0x{{[0-9a-f]+}} <col:66, col:88> +constexpr int* construct_at(int* p, int v) { [[msvc::constexpr]] return ::new (p) int(v); } + +constexpr bool check_construct_at() { int x; return *construct_at(&x, 42) == 42; } + +static_assert(check_construct_at()); Index: clang/test/AST/msvc-attrs.cpp =================================================================== --- /dev/null +++ clang/test/AST/msvc-attrs.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -fms-extensions -std=c++20 -ast-dump %s | FileCheck %s + +// [[msvc::constexpr]] tests + +// MSConstexprDocs (1) +// CHECK: used f1 'bool ()' +// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:3, col:9> +[[msvc::constexpr]] bool f1() { return true; } + +// MSConstexprDocs (2) +// CHECK: used constexpr f2 'bool ()' +// CHECK: AttributedStmt 0x{{[0-9a-f]+}} <col:23, col:53> +// CHECK-NEXT: MSConstexprAttr 0x{{[0-9a-f]+}} <col:25, col:31> +// CHECK-NEXT: ReturnStmt 0x{{[0-9a-f]+}} <col:43, col:53> +constexpr bool f2() { [[msvc::constexpr]] return f1(); } + +static_assert(f2()); + +constexpr bool f3() { [[msvc::constexpr]] return f1() || f1() && f1(); } +static_assert(f3()); + +[[msvc::constexpr]] int f4(int x) { [[msvc::constexpr]] return x > 1 ? 1 + f4(x / 2) : 0; } +constexpr bool f5() { [[msvc::constexpr]] return f4(32) == 5; } +static_assert(f5()); + +[[msvc::constexpr]] int f6(int x) +{ + switch (x) + { + case 42: return 1337; + default: + if (x < 0) [[msvc::constexpr]] return f4(-x); + else return x; + } +} + +constexpr bool f7() { [[msvc::constexpr]] return f6(f6(42) - 1337 + f6(-32) - 5 + (f6(1) ? f6(0) : f6(2))) == f6(0); } +static_assert(f7()); + +constexpr bool f8() { return true; } +[[msvc::constexpr]] bool f9() { return f8(); } +constexpr bool f10() { [[msvc::constexpr]] return f9(); } +static_assert(f10()); + +struct S1 { + [[msvc::constexpr]] virtual bool vm() { return true; } + constexpr bool cm() { [[msvc::constexpr]] return vm(); } +}; +static_assert(S1{}.cm()); + +/* +// TODO: Add support for [[msvc::constexpr]] constructor +struct S2 { + [[msvc::constexpr]] S2() {} + [[msvc::constexpr]] bool value() { return true; } + static constexpr bool check() { [[msvc::constexpr]] return S2{}.value(); } +}; +static_assert(S2::check()); +*/ Index: clang/test/AST/msvc-attrs-invalid.cpp =================================================================== --- /dev/null +++ clang/test/AST/msvc-attrs-invalid.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -fms-extensions -std=c++20 -verify %s +// RUN: %clang_cc1 -fms-extensions -std=c++17 -verify=cpp17 %s + +#if __cplusplus >= 202002L +// Check explicitly invalid code + +void runtime() {} // expected-note {{declared here}} + +[[msvc::constexpr]] void f0() { runtime(); } // expected-error {{[[msvc::constexpr]] function never produces a constant expression}} \ + // expected-note {{non-constexpr function 'runtime' cannot be used in a constant expression}} +[[msvc::constexpr]] constexpr void f1() {} // expected-error {{[[msvc::constexpr]] cannot be applied to a constexpr function 'f1'}} +[[msvc::constexpr]] consteval void f2() {} // expected-error {{[[msvc::constexpr]] cannot be applied to a consteval function 'f2'}} + +struct B1 {}; +struct D1 : virtual B1 { // expected-note {{virtual base class declared here}} + [[msvc::constexpr]] D1() {} // expected-error {{constexpr constructor not allowed in struct with virtual base class}} +}; + +struct [[msvc::constexpr]] S2{}; // expected-error {{'constexpr' attribute only applies to functions and statements}} + +// Check invalid code mixed with valid code + +[[msvc::constexpr]] int f4(int x) { return x > 1 ? 1 + f4(x / 2) : 0; } // expected-note {{[[msvc::constexpr]] function 'f4' is used not in '[[msvc::constexpr]] return' statement}} +constexpr bool f5() { [[msvc::constexpr]] return f4(32) == 5; } // expected-note {{in call to 'f4(32)'}} +static_assert(f5()); // expected-error {{static assertion expression is not an integral constant expression}} \ + // expected-note {{in call to 'f5()'}} + +int f6(int x) { [[msvc::constexpr]] return x > 1 ? 1 + f6(x / 2) : 0; } // expected-note {{declared here}} \ + // expected-note {{declared here}} +constexpr bool f7() { [[msvc::constexpr]] return f6(32) == 5; } // expected-error {{constexpr function never produces a constant expression}} \ + // expected-note {{non-constexpr function 'f6' cannot be used in a constant expression}} \ + // expected-note {{non-constexpr function 'f6' cannot be used in a constant expression}} +static_assert(f7()); // expected-error {{static assertion expression is not an integral constant expression}} \ + // expected-note {{in call to 'f7()'}} + +constexpr bool f8() { // expected-error {{constexpr function never produces a constant expression}} + [[msvc::constexpr]] f4(32); // expected-warning {{[[msvc::constexpr]] has effect only on function definitions and return statements}} \ + // expected-note {{[[msvc::constexpr]] function 'f4' is used not in '[[msvc::constexpr]] return' statement}} \ + // expected-note {{[[msvc::constexpr]] function 'f4' is used not in '[[msvc::constexpr]] return' statement}} + [[msvc::constexpr]] int i5 = f4(32); // expected-error {{'constexpr' attribute only applies to functions and statements}} + return i5 == 5; +} +static_assert(f8()); // expected-error {{static assertion expression is not an integral constant expression}} \ + // expected-note {{in call to 'f8()'}} +#else + +struct S1 { + [[msvc::constexpr]] virtual bool vm() const { return true; } // cpp17-error {{virtual function cannot be [[msvc::constexpr]]}} +}; + +#endif Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -322,6 +322,13 @@ return ::new (S.Context) UnlikelyAttr(S.Context, A); } +static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A, + SourceRange Range) { + if (!isa<ReturnStmt>(St)) + S.Diag(A.getLoc(), diag::warn_ms_constexpr_no_effect); + return ::new (S.Context) MSConstexprAttr(S.Context, A); +} + #define WANT_STMT_MERGE_LOGIC #include "clang/Sema/AttrParsedAttrImpl.inc" #undef WANT_STMT_MERGE_LOGIC @@ -523,6 +530,8 @@ return handleLikely(S, St, A, Range); case ParsedAttr::AT_Unlikely: return handleUnlikely(S, St, A, Range); + case ParsedAttr::AT_MSConstexpr: + return handleMSConstexprAttr(S, St, A, Range); default: // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a // declaration attribute is not written on a statement, but this code is Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -2425,9 +2425,12 @@ SmallVector<PartialDiagnosticAt, 8> Diags; if (Kind == Sema::CheckConstexprKind::Diagnose && !Expr::isPotentialConstantExpr(Dcl, Diags)) { + int ConstKind = Dcl->hasAttr<MSConstexprAttr>() ? 2 + : Dcl->isConsteval() ? 1 + : 0; SemaRef.Diag(Dcl->getLocation(), diag::ext_constexpr_function_never_constant_expr) - << isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval(); + << isa<CXXConstructorDecl>(Dcl) << ConstKind; for (size_t I = 0, N = Diags.size(); I != N; ++I) SemaRef.Diag(Diags[I].first, Diags[I].second); // Don't return false here: we allow this for compatibility in Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -7316,6 +7316,22 @@ D->addAttr(::new (S.Context) ThreadAttr(S.Context, AL)); } +static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + auto *FD = cast<FunctionDecl>(D); + if (FD->isConstexprSpecified() || FD->isConsteval()) { + S.Diag(AL.getLoc(), diag::err_ms_constexpr_not_distinct) + << FD->isConsteval() << FD; + return; + } + if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { + if (!S.getLangOpts().CPlusPlus20 && MD->isVirtual()) { + S.Diag(AL.getLoc(), diag::err_ms_constexpr_virtual); + return; + } + } + D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL)); +} + static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) { SmallVector<StringRef, 4> Tags; for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) { @@ -9286,6 +9302,9 @@ case ParsedAttr::AT_Thread: handleDeclspecThreadAttr(S, D, AL); break; + case ParsedAttr::AT_MSConstexpr: + handleMSConstexprAttr(S, D, AL); + break; // HLSL attributes: case ParsedAttr::AT_HLSLNumThreads: Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -15908,7 +15908,9 @@ ActivePolicy = &WP; } - if (!IsInstantiation && FD && FD->isConstexpr() && !FD->isInvalidDecl() && + if (!IsInstantiation && FD && + (FD->isConstexpr() || FD->hasAttr<MSConstexprAttr>()) && + !FD->isInvalidDecl() && !CheckConstexprFunctionDefinition(FD, CheckConstexprKind::Diagnose)) FD->setInvalidDecl(); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -639,6 +639,10 @@ return false; } + /// Whether we're in a context where [[msvc::constexpr]] evaluation is + /// permitted. See MSConstexprDocs for description of permitted contexts. + bool CanEvalMSConstexpr = false; + private: APValue &createLocal(APValue::LValueBase Base, const void *Key, QualType T, ScopeKind Scope); @@ -672,6 +676,19 @@ private: llvm::TimeTraceScope TimeScope; }; + + /// RAII object used to change the current ability of + /// [[msvc::constexpr]] evaulation. + struct MSConstexprContextRAII { + CallStackFrame &Frame; + bool OldValue; + explicit MSConstexprContextRAII(CallStackFrame &Frame, bool Value) + : Frame(Frame), OldValue(Frame.CanEvalMSConstexpr) { + Frame.CanEvalMSConstexpr = Value; + } + + ~MSConstexprContextRAII() { Frame.CanEvalMSConstexpr = OldValue; } + }; } static bool HandleDestruction(EvalInfo &Info, const Expr *E, @@ -5505,11 +5522,16 @@ case Stmt::LabelStmtClass: return EvaluateStmt(Result, Info, cast<LabelStmt>(S)->getSubStmt(), Case); - case Stmt::AttributedStmtClass: - // As a general principle, C++11 attributes can be ignored without - // any semantic impact. - return EvaluateStmt(Result, Info, cast<AttributedStmt>(S)->getSubStmt(), - Case); + case Stmt::AttributedStmtClass: { + const auto *AS = cast<AttributedStmt>(S); + const auto *SS = AS->getSubStmt(); + MSConstexprContextRAII msConstexprContext( + *Info.CurrentCall, + Info.getLangOpts().MicrosoftExt && + hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) && + isa<ReturnStmt>(SS)); + return EvaluateStmt(Result, Info, SS, Case); + } case Stmt::CaseStmtClass: case Stmt::DefaultStmtClass: @@ -5580,8 +5602,29 @@ } // Can we evaluate this function call? - if (Definition && Definition->isConstexpr() && Body) - return true; + if (Definition && Body) { + if (Definition->isConstexpr()) + return true; + + bool canEvalMSConstexpr = Info.CurrentCall->CanEvalMSConstexpr; + bool isMSConstexpr = Definition->hasAttr<MSConstexprAttr>(); + if (canEvalMSConstexpr && isMSConstexpr) + return true; + + if (canEvalMSConstexpr || isMSConstexpr) { + // Diagnose invalid usage of [[msvc::constexpr]] function + bool isConstructor = isa<CXXConstructorDecl>(Definition); + if (canEvalMSConstexpr) { // !isMSConstexpr + Info.FFDiag(CallLoc, diag::note_constexpr_invalid_function, 1) + << /*IsConstexpr*/ 0 << isConstructor << Definition; + Info.Note(Definition->getLocation(), diag::note_declared_at); + } else if (isMSConstexpr) { // !canEvalMSConstexpr + Info.FFDiag(CallLoc, diag::note_ms_constexpr_invalid_context) + << isConstructor << Definition; + } + return false; + } + } if (Info.getLangOpts().CPlusPlus11) { const FunctionDecl *DiagDecl = Definition ? Definition : Declaration; @@ -9544,8 +9587,10 @@ bool IsNothrow = false; bool IsPlacement = false; + bool IsMSConstexpr = Info.CurrentCall->CanEvalMSConstexpr && + OperatorNew->hasAttr<MSConstexprAttr>(); if (OperatorNew->isReservedGlobalPlacementOperator() && - Info.CurrentCall->isStdFunction() && !E->isArray()) { + (Info.CurrentCall->isStdFunction() || IsMSConstexpr) && !E->isArray()) { // FIXME Support array placement new. assert(E->getNumPlacementArgs() == 1); if (!EvaluatePointer(E->getPlacementArg(0), Result, Info)) Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2789,8 +2789,8 @@ "is incompatible with C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore; def ext_constexpr_function_never_constant_expr : ExtWarn< - "%select{constexpr|consteval}1 %select{function|constructor}0 never produces a " - "constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError; + "%select{constexpr|consteval|[[msvc::constexpr]]}1 %select{function|constructor}0 " + "never produces a constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError; def err_attr_cond_never_constant_expr : Error< "%0 attribute expression never produces a constant expression">; def err_diagnose_if_invalid_diagnostic_type : Error< @@ -2812,6 +2812,12 @@ InGroup<CXXPre14Compat>, DefaultIgnore; def note_constexpr_body_previous_return : Note< "previous return statement is here">; +def err_ms_constexpr_not_distinct : Error< + "[[msvc::constexpr]] cannot be applied to a %select{constexpr|consteval}0 function %1">; +def err_ms_constexpr_virtual : Error<"virtual function cannot be [[msvc::constexpr]]">; +def warn_ms_constexpr_no_effect : Warning< + "[[msvc::constexpr]] has effect only on function definitions and return statements">, + InGroup<IgnoredAttributes>; // C++20 function try blocks in constexpr def ext_constexpr_function_try_block_cxx20 : ExtWarn< Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -389,6 +389,9 @@ "type %0 has unexpected layout">; def note_constexpr_unsupported_flexible_array : Note< "flexible array initialization is not yet supported">; +def note_ms_constexpr_invalid_context : Note< + "[[msvc::constexpr]] %select{function|constructor}0 %1 is used not in " + "'[[msvc::constexpr]] return' statement">; def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -3593,6 +3593,19 @@ }]; } +def MSConstexprDocs : Documentation { + let Category = DocCatStmt; + let Content = [{ +The ``[[msvc::constexpr]]`` attribute can be applied only to a function +definition or a return statement. It has no effect on function declarations. +This attribute permits constant evaluation of ``[[msvc::constexpr]]`` functions +in ``[[msvc::constexpr]] return`` statements inside ``constexpr`` or +``[[msvc::constexpr]]`` functions. + +This attribute is available only as Microsoft extension (``-fms-extensions``). + }]; +} + def MSNoVTableDocs : Documentation { let Category = DocCatDecl; let Content = [{ Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3530,6 +3530,14 @@ // Microsoft-related attributes +def MSConstexpr : InheritableAttr { + let LangOpts = [MicrosoftExt]; + let Spellings = [CXX11<"msvc", "constexpr">]; + let Subjects = SubjectList<[Function, Stmt], ErrorDiag, + "functions and statements">; + let Documentation = [MSConstexprDocs]; +} + def MSNoVTable : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> { let Spellings = [Declspec<"novtable">]; let Subjects = SubjectList<[CXXRecord]>;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits