RIscRIpt updated this revision to Diff 515740.
RIscRIpt added a comment.
A proper implementation
In D134475#4224082 <https://reviews.llvm.org/D134475#4224082>, @erichkeane
wrote:
> I don't have a good idea, I don't think any sort of RAII object is the right
> way (since it will be wrong on child calls/etc), and the ParentMap is
> definitely not the right way. Perhaps just a flag for
> CheckConstexprFunction? I've not spent too much time in this section of the
> compiler, so hopefully someone else can come along and help. I suspect it is
> a property of `CallStackFrame`? But that already contains a link to the
> FucntionDecl, right? As far as the 'on return statement', I think the bit on
> CallStackFrame/EvalInfo is probably what is necessary.
> But that already contains a link to the FucntionDecl, right?
Yes, but we need to know current context (whether we're in `[[msvc::constexpr]]
return` statement).
> As far as the 'on return statement', I think the bit on
> CallStackFrame/EvalInfo is probably what is necessary.
That's what I ended-up doing. This was the most reasonable approach I could
come-up with.
I added a property to `CallStackFrame`, because it's a property of current
function: whether we are in valid context which permits evaluation of
`[[msvc::constexpr]]`. Additionally I added `MSConstexprContextRAII` which is
used in `case Stmt::AttributedStmtClass:` to alter the property.
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
@@ -83,6 +83,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,38 @@
+// 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());
Index: clang/test/AST/msvc-attrs-invalid.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/msvc-attrs-invalid.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -fms-extensions -std=c++20 -verify %s
+
+// 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'}}
+
+// Check invalid code mixed with valid code
+
+[[msvc::constexpr]] bool undefined();
+
+[[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()'}}
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
@@ -521,6 +528,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
@@ -2424,9 +2424,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
@@ -7207,6 +7207,16 @@
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;
+ }
+ 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) {
@@ -9156,6 +9166,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
@@ -15822,7 +15822,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
@@ -635,6 +635,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);
@@ -668,6 +672,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,
@@ -5483,11 +5500,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:
@@ -5558,8 +5580,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;
@@ -9514,8 +9557,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
@@ -2770,8 +2770,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<
@@ -2793,6 +2793,11 @@
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 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
@@ -385,6 +385,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
@@ -3535,6 +3535,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
@@ -3496,6 +3496,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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits