-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang]: reflection operator parsing for primitive types #164692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
implement views::concat (P2542)
Revert "implement views::concat (P2542) "
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-modules Author: Nhat Nguyen (changkhothuychung) ChangesPatch is 23.30 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/164692.diff 28 Files Affected:
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 5f16bac94d5e6..5c8b2209d5364 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5493,6 +5493,56 @@ class BuiltinBitCastExpr final
}
};
+/// Represents a C++2c reflect expression (P2996).
+class CXXReflectExpr : public Expr {
+
+ // Source locations.
+ SourceLocation OperatorLoc;
+ SourceRange OperandRange;
+
+ CXXReflectExpr(const ASTContext &C, QualType T, QualType Ty);
+ CXXReflectExpr(const ASTContext &C, QualType T, Decl *Arg, bool IsNamespace);
+ CXXReflectExpr(EmptyShell Empty);
+
+public:
+
+ static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc,
+ SourceLocation ArgLoc, QualType Operand);
+
+ static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc,
+ SourceLocation OperandLoc, Decl *Operand);
+
+ static CXXReflectExpr *CreateEmpty(ASTContext& C);
+
+ SourceLocation getBeginLoc() const LLVM_READONLY { return OperatorLoc; }
+ SourceLocation getEndLoc() const LLVM_READONLY {
+ return OperandRange.getEnd();
+ }
+ SourceRange getSourceRange() const {
+ return SourceRange(getBeginLoc(), getEndLoc());
+ }
+
+ /// Returns location of the '^^'-operator.
+ SourceLocation getOperatorLoc() const { return OperatorLoc; }
+ SourceRange getOperandRange() const { return OperandRange; }
+
+ /// Sets the location of the '^^'-operator.
+ void setOperatorLoc(SourceLocation L) { OperatorLoc = L; }
+ void setOperandRange(SourceRange R) { OperandRange = R; }
+
+ child_range children() {
+ return child_range(child_iterator(), child_iterator());
+ }
+
+ const_child_range children() const {
+ return const_child_range(const_child_iterator(), const_child_iterator());
+ }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXReflectExprClass;
+ }
+};
+
} // namespace clang
#endif // LLVM_CLANG_AST_EXPRCXX_H
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 7a2881f6124f3..7b8a678ffb861 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2883,6 +2883,10 @@ DEF_TRAVERSE_STMT(CXXUnresolvedConstructExpr, {
TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
})
+DEF_TRAVERSE_STMT(CXXReflectExpr, {
+ // TODO
+})
+
// These expressions all might take explicit template arguments.
// We traverse those if so. FIXME: implement these.
DEF_TRAVERSE_STMT(CXXConstructExpr, {})
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c724136a7fdaf..ea81a2332cd50 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1848,6 +1848,11 @@ def err_placeholder_expected_auto_or_decltype_auto : Error<
"expected 'auto' or 'decltype(auto)' after concept name">;
}
+let CategoryName = "Reflection Issue" in {
+def err_cannot_reflect_operand : Error<
+ "cannot reflect the provided operand">;
+}
+
def warn_max_tokens : Warning<
"the number of preprocessor source tokens (%0) exceeds this token limit (%1)">,
InGroup<MaxTokens>, DefaultIgnore;
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 0e91b42a132c1..c9d24430326ff 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -380,6 +380,8 @@ FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVT
FEATURE(clang_atomic_attributes, true)
+FEATURE(reflection, LangOpts.Reflection)
+
// CUDA/HIP Features
FEATURE(cuda_noinline_keyword, LangOpts.CUDA)
EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates)
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 84f5ab3443a59..b2051fb536432 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -498,6 +498,7 @@ LANGOPT(BoundsSafety, 1, 0, NotCompatible, "Bounds safety extension for C")
LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, "Experimental lifetime safety analysis for C++")
LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type")
+LANGOPT(Reflection , 1, 0, NotCompatible, "C++26 Reflection")
#undef LANGOPT
#undef ENUM_LANGOPT
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index bf3686bb372d5..987e1d1408e06 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -177,6 +177,9 @@ def CoyieldExpr : StmtNode<CoroutineSuspendExpr>;
def ConceptSpecializationExpr : StmtNode<Expr>;
def RequiresExpr : StmtNode<Expr>;
+// c++ 26 reflection
+def CXXReflectExpr : StmtNode<Expr>;
+
// Obj-C Expressions.
def ObjCStringLiteral : StmtNode<Expr>;
def ObjCBoxedExpr : StmtNode<Expr>;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 564d6010181cc..b7fef8b2de739 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -233,6 +233,7 @@ PUNCTUATOR(greatergreater, ">>")
PUNCTUATOR(greaterequal, ">=")
PUNCTUATOR(greatergreaterequal, ">>=")
PUNCTUATOR(caret, "^")
+PUNCTUATOR(caretcaret, "^^")
PUNCTUATOR(caretequal, "^=")
PUNCTUATOR(pipe, "|")
PUNCTUATOR(pipepipe, "||")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6245cf33a0719..1880459fab52f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3663,6 +3663,11 @@ defm application_extension : BoolFOption<"application-extension",
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Restrict code to those available for App Extensions">,
NegFlag<SetFalse>>;
+defm reflection : BoolFOption<"reflection",
+ LangOpts<"Reflection">, DefaultFalse,
+ PosFlag<SetTrue, [], [ClangOption, CC1Option],
+ "Enable C++26 reflection">,
+ NegFlag<SetFalse>>;
defm sized_deallocation : BoolFOption<"sized-deallocation",
LangOpts<"SizedDeallocation">, Default<cpp14.KeyPath>,
PosFlag<SetTrue, [], [], "Enable C++14 sized global deallocation functions">,
@@ -7153,7 +7158,7 @@ defm android_pad_segment : BooleanFFlag<"android-pad-segment">, Group<f_Group>;
def shared_libflangrt : Flag<["-"], "shared-libflangrt">,
HelpText<"Link the flang-rt shared library">, Group<Link_Group>,
Visibility<[FlangOption]>, Flags<[NoArgumentUnused]>;
-def static_libflangrt : Flag<["-"], "static-libflangrt">,
+def static_libflangrt : Flag<["-"], "static-libflangrt">,
HelpText<"Link the flang-rt static library">, Group<Link_Group>,
Visibility<[FlangOption]>, Flags<[NoArgumentUnused]>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index e301cf1080977..9b8d8f7633f4d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -150,6 +150,7 @@ enum class TentativeCXXTypeIdContext {
AsTemplateArgument,
InTrailingReturnType,
AsGenericSelectionArgument,
+ AsReflectionOperand
};
/// The kind of attribute specifier we have found.
@@ -5167,6 +5168,11 @@ class Parser : public CodeCompletionHandler {
/// Implementations are in ParseHLSL.cpp
///@{
+
+ //===--------------------------------------------------------------------===//
+ // C++2c: Reflection [P2996]
+ ExprResult ParseCXXReflectExpression(SourceLocation OpLoc);
+
private:
bool MaybeParseHLSLAnnotations(Declarator &D,
SourceLocation *EndLoc = nullptr,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f53aafdeb4f36..d07c1160023b3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14663,6 +14663,17 @@ class Sema final : public SemaBase {
/// Implementations are in SemaConcept.cpp
///@{
+public:
+
+ ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, TypeSourceInfo* T);
+ ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc,
+ SourceLocation ArgLoc, Decl *D);
+
+ ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc,
+ SourceLocation OperandLoc, QualType T);
+ ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc,
+ SourceLocation OperandLoc, Decl *D);
+
public:
void PushSatisfactionStackEntry(const NamedDecl *D,
const llvm::FoldingSetNodeID &ID) {
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 5d09d5536e5ab..b950c444d9aa2 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1925,6 +1925,9 @@ enum StmtCode {
EXPR_CONCEPT_SPECIALIZATION, // ConceptSpecializationExpr
EXPR_REQUIRES, // RequiresExpr
+ // Reflection
+ EXPR_REFLECT,
+
// CUDA
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 95de6a82a5270..b4758465f669a 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1939,6 +1939,40 @@ TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C,
return new (Mem) TypeTraitExpr(EmptyShell(), IsStoredAsBool);
}
+CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, QualType Ty)
+: Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary) {}
+
+CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType T, Decl *Arg, bool IsNamespace)
+: Expr(CXXReflectExprClass, T, VK_PRValue, OK_Ordinary) {}
+
+CXXReflectExpr::CXXReflectExpr(EmptyShell Empty)
+: Expr(CXXReflectExprClass, Empty) {}
+
+CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, SourceLocation OperatorLoc,
+ SourceLocation OperandLoc, QualType Operand) {
+ CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.DependentTy, Operand);
+ E->setOperatorLoc(OperatorLoc);
+ E->setOperandRange(OperandLoc);
+ return E;
+}
+
+CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C,
+ SourceLocation OperatorLoc,
+ SourceLocation OperandLoc,
+ Decl *Operand) {
+ bool IsNamespace = isa<TranslationUnitDecl>(Operand);
+
+ CXXReflectExpr *E = new (C) CXXReflectExpr(C, C.DependentTy, Operand,
+ IsNamespace);
+ E->setOperatorLoc(OperatorLoc);
+ E->setOperandRange(OperandLoc);
+ return E;
+}
+
+CXXReflectExpr *CXXReflectExpr::CreateEmpty(ASTContext &C) {
+ return new (C) CXXReflectExpr(EmptyShell());
+}
+
CUDAKernelCallExpr::CUDAKernelCallExpr(Expr *Fn, CallExpr *Config,
ArrayRef<Expr *> Args, QualType Ty,
ExprValueKind VK, SourceLocation RP,
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index aeacd0dc765ef..4c53c316e989a 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -216,6 +216,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::SourceLocExprClass:
case Expr::ConceptSpecializationExprClass:
case Expr::RequiresExprClass:
+ case Expr::CXXReflectExprClass:
return Cl::CL_PRValue;
case Expr::EmbedExprClass:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b706b14945b6d..0f14aa3c7258f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -18295,6 +18295,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::ArrayTypeTraitExprClass:
case Expr::ExpressionTraitExprClass:
case Expr::CXXNoexceptExprClass:
+ case Expr::CXXReflectExprClass:
return NoDiag();
case Expr::CallExprClass:
case Expr::CXXOperatorCallExprClass: {
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 2173aed5b45af..18bd38c66b5e9 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4945,6 +4945,12 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
E = cast<ConstantExpr>(E)->getSubExpr();
goto recurse;
+ case Expr::CXXReflectExprClass: {
+ // TODO: implement this after introducing std::meta::info
+ // and add info in APValue
+ break;
+ }
+
// FIXME: invent manglings for all these.
case Expr::BlockExprClass:
case Expr::ChooseExprClass:
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 586c3000f105c..8e5bab721e8e4 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2566,6 +2566,12 @@ void StmtPrinter::VisitCXXUnresolvedConstructExpr(
OS << ')';
}
+
+void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
+ // TODO: Make this better.
+ OS << "^(...)";
+}
+
void StmtPrinter::VisitCXXDependentScopeMemberExpr(
CXXDependentScopeMemberExpr *Node) {
if (!Node->isImplicitAccess()) {
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 589a156a2b6ea..ad37b5e71472e 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2164,6 +2164,11 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
ID.AddInteger(Hasher.CalculateHash());
}
+void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) {
+ VisitExpr(E);
+ // TODO:
+}
+
void
StmtProfiler::VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *S) {
VisitExpr(S);
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index b282a600c0e56..5df36d041d0c1 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -4348,6 +4348,9 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
if (Char == '=') {
CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
Kind = tok::caretequal;
+ } else if (LangOpts.Reflection && Char == '^') {
+ CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
+ Kind = tok::caretcaret;
} else {
if (LangOpts.OpenCL && Char == '^')
Diag(CurPtr, diag::err_opencl_logical_exclusive_or);
diff --git a/clang/lib/Parse/CMakeLists.txt b/clang/lib/Parse/CMakeLists.txt
index e6cbf3b868b7d..8dd120f529b13 100644
--- a/clang/lib/Parse/CMakeLists.txt
+++ b/clang/lib/Parse/CMakeLists.txt
@@ -26,6 +26,7 @@ add_clang_library(clangParse
ParseTentative.cpp
Parser.cpp
ParseOpenACC.cpp
+ ParseReflect.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 3515343202de1..22963d985b01b 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1208,6 +1208,13 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand,
AllowSuffix = false;
Res = ParseUnaryExprOrTypeTraitExpression();
break;
+ case tok::caretcaret: {
+ if (getLangOpts().Reflection) {
+ SourceLocation FirstCaret = ConsumeToken(); // eat first '^'
+ Res = ParseCXXReflectExpression(/*OpLoc=*/FirstCaret);
+ }
+ break;
+ }
case tok::ampamp: { // unary-expression: '&&' identifier
if (NotPrimaryExpression)
*NotPrimaryExpression = true;
@@ -2249,6 +2256,9 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
else if (getLangOpts().C2y && OpTok.is(tok::kw__Countof))
Diag(OpTok, diag::warn_c2y_compat_keyword) << OpTok.getName();
+ if (OpTok.is(tok::caretcaret))
+ return ParseCXXReflectExpression(OpTok.getLocation());
+
EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::Unevaluated,
Sema::ReuseLambdaContextDecl);
diff --git a/clang/lib/Parse/ParseReflect.cpp b/clang/lib/Parse/ParseReflect.cpp
new file mode 100644
index 0000000000000..8c974d949d87f
--- /dev/null
+++ b/clang/lib/Parse/ParseReflect.cpp
@@ -0,0 +1,61 @@
+//===--- ParseReflect.cpp - C++2c Reflection Parsing ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements parsing for reflection facilities.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/LocInfoType.h"
+#include "clang/Basic/DiagnosticParse.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
+using namespace clang;
+
+ExprResult Parser::ParseCXXReflectExpression(SourceLocation OpLoc) {
+ assert(Tok.is(tok::caretcaret) && "expected '^^'");
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+
+ SourceLocation OperandLoc = Tok.getLocation();
+
+ // Parse a leading nested-name-specifier
+ CXXScopeSpec SS;
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/false)) {
+ SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+ return ExprError();
+ }
+
+ {
+ TentativeParsingAction TPA(*this);
+
+ if (SS.isValid() &&
+ SS.getScopeRep().getKind() == NestedNameSpecifier::Kind::Global) {
+ // Check for global namespace '^^::'
+ TPA.Commit();
+ Decl *TUDecl = Actions.getASTContext().getTranslationUnitDecl();
+ return Actions.ActOnCXXReflectExpr(OpLoc, SourceLocation(), TUDecl);
+ }
+ TPA.Revert();
+ }
+
+ if (isCXXTypeId(TentativeCXXTypeIdContext::AsReflectionOperand)) {
+ TypeResult TR = ParseTypeName(/*TypeOf=*/nullptr);
+ if (TR.isInvalid())
+ return ExprError();
+
+ TypeSourceInfo *TSI = nullptr;
+ QualType QT = Actions.GetTypeFromParser(TR.get(), &TSI);
+
+ return Actions.ActOnCXXReflectExpr(OpLoc, TSI);
+ }
+
+ Diag(OperandLoc, diag::err_cannot_reflect_operand);
+ return ExprError();
+}
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 82f2294ff5bb7..8a3ae2232767e 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -574,6 +574,9 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
} else if (Context == TentativeCXXTypeIdContext::InTrailingReturnType) {
TPR = TPResult::True;
isAmbiguous = true;
+ } else if (Context == TentativeCXXTypeIdContext::AsReflectionOperand) {
+ TPR = TPResult::True;
+ isAmbiguous = true;
} else
TPR = TPResult::False;
}
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index a0483c3027199..ff8d2139289a3 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1379,6 +1379,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Expr::CXXNoexceptExprClass:
case Expr::CXXNullPtrLiteralExprClass:
case Expr::CXXPseudoDestructorExprClass:
+ case Expr::CXXReflectExprClass:
case Expr::CXXScalarValueInitExprClass:
case Expr::CXXThisExprClass:
case Expr::CXXUuidofExprClass:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 06b2529011c74..95bc91ab29f90 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -17760,6 +17760,25 @@ void Sema::PushExpressionEvaluationContextForFunction(
}
}
+ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc, TypeSourceInfo* TSI) {
+ return BuildCXXReflectExpr(OpLoc, TSI->getTypeLoc().getBeginLoc(), TSI->getType());
+}
+
+ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc,
+ SourceLocation ArgLoc, Decl *D) {
+ return BuildCXXReflectExpr(OpLoc, ArgLoc, D);
+}
+
+ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc,
+ ...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
I think we are generally in favor of that direction, and yes, such a patch would be a good initial patch. I spoke briefly with Dan a few minutes ago, and think that a patch that introduced the basics of 'parsing' (that just did consume + diagnostic + ignore) would be an actionable first set, then we can start adding the AST node as we handle 1 thing at a time. I think that would be eminently more manageable. |
What does this "ignore" look like? I am concerned if we "parse" but are unable to demonstrate that the tokens are accepted in the right grammar contexts. Perhaps we can emit into the AST as if it was a |
clang/include/clang/AST/ExprCXX.h
Outdated
| static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, | ||
| SourceLocation ArgLoc, QualType Operand); | ||
|
|
||
| static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The grammar says
reflect-expression:
^^ ::
^^ reflection-name
^^ type-id
^^ id-expression
reflection-name:
nested-name-specifier identifier
nested-name-specifier template identifier
To model that we should have overloads for
- TypeLoc
- Expr*
For Namespaces, I think we can reuse NestedNameSpecifierLoc - to check
For declarations we need to a way to store
- the location of the identifier
- the loc of any
templatekeyword
We might want to introduce a new struct for that
{
NestedNameSpecifierLoc NNS;
SourceLocation IdLoc;
SourceLocation TemplateKWLoc = {};
}
But maybe that would be reinventing DeclRefExpr - Can we reuse DeclRefExpr? That would be neat. I don't think DeclRefExpr is meant to support namespaces, aliases, etc though.
clang/lib/Parse/ParseReflect.cpp
Outdated
| SS.getScopeRep().getKind() == NestedNameSpecifier::Kind::Global) { | ||
| // global namespace ::. | ||
| Decl *TUDecl = Actions.getASTContext().getTranslationUnitDecl(); | ||
| return Actions.ActOnCXXReflectExpr(DoubleCaretLoc, SourceLocation(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are 2 relevant source locations here
- the location of the declaration
- the location of
::which we definitively want to keep - cf above comment for ways we could do that`
clang/lib/Parse/ParseReflect.cpp
Outdated
| if (Tok.isOneOf(tok::identifier, tok::kw_operator, tok::kw_template, | ||
| tok::tilde, tok::annot_template_id)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be other relevant annotations here (like type annotation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cor3ntin are you referring to tok::annot_typename and tok::annot_decltype ?
|
IMO the 'parser errors' are sufficient enough for me to show that the parsing contexts/etc are well enough developed to merge. IF there is a mistake, we can always fix it up later. I did the same with the OpenACC stuff when doing the implementation, and we did the same with concepts when it joined clang. Simply emitting the parser errors/not calling into SEMA at all is sufficient to demonstrate that we are handling the text correctly. IF we wanted to add the 'std::meta' type and have it always just toss in a But my big fear is that I'm expected to review huge patches that I'll never get through them, and as a template maintainer, this is stuff that I SHOULD be doing. I'd rather small patches that don't really show much/any functionality that I can review in a short amount of time, vs something that takes me several hours (like this one) to look at every time I context switch back. |
Ah I see, thanks! Yeah it should be off by default. |
|
|
||
| let CategoryName = "Reflection Issue" in { | ||
| def err_cannot_reflect_operand : Error< | ||
| "expected reflectable entity">; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we think of better wording for this? The diagnostic doesn't really help the user to understand how to repair their code -- if they passed in something wrong, they likely don't know what a "reflectable entity" is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now this PR only supports parsing for global namespace and primitive types. If a diagnostics is hit, it could be because the operand is invalid, or it is not yet implemented. The parsing logic is incomplete, so its hard to distinguish the two's for now. I proposed I would complete the parsing logic in the next PR, so we would have a better idea how we want to provide wordings for the diagnostics.
If you have any suggestions for now, please let me know, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its probably ok to leave this really awful for now, I have no idea what it means either,, but I'm hopeful this'll improve.
DEPENDING on how long we intend this to last, we might think about saying something like, unknown or unimplemented reflectable entity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the message to unknown or unimplemented reflectable entity. Depending on how we want to scope the next PR, I can work on parsing the remaining items in reflect-expression, by then we will have a better idea how we want to have diagnostics messages in specific cases.
| @@ -499,6 +498,7 @@ LANGOPT(BoundsSafety, 1, 0, NotCompatible, "Bounds safety extension for C") | |||
| LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, "Experimental lifetime safety analysis for C++") | |||
|
|
|||
| LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type") | |||
| LANGOPT(Reflection , 1, 0, NotCompatible, "C++26 Reflection") | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I (at least partially) disagree. Reflection is so experimental I think it should be an option, but one that can only be enabled in C++26 mode. Given that the feature is changing in the last few minutes of getting C++26 out the door, I think we should treat reflection like we would a TS to some degree so users can opt out of it, especially because of the potential to break code (there's still parsing ambiguities with ^^ and blocks, though far less of them than there were with ^ as the operator).
| @@ -3689,6 +3663,11 @@ defm application_extension : BoolFOption<"application-extension", | |||
| PosFlag<SetTrue, [], [ClangOption, CC1Option], | |||
| "Restrict code to those available for App Extensions">, | |||
| NegFlag<SetFalse>>; | |||
| defm reflection : BoolFOption<"reflection", | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense as a CC1-only option rather than a driver option. aka. the driver can say "C++26 mode? enable reflection by default", but there's still a hidden escape hatch to say -Xclang -fno-reflection if you need to. I think the frontend should reject -freflection outside of -std=c++26 (or later) modes, so this isn't a backdoor way to enable reflection in older language modes. WDYT?
| E = cast<ConstantExpr>(E)->getSubExpr(); | ||
| goto recurse; | ||
|
|
||
| case Expr::CXXReflectExprClass: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we need a similar case for the Microsoft mangler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@katzdm can you help me on this question?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@changkhothuychung Yes, absolutely; analogous changes should be made to MicrosoftMangle.cpp.
Some context: ItaniumMangle.cpp and MicrosoftMangle.cpp implement two different schemes for mangling the name of a C++ entity; the former implements the Itanium C++ ABI, whereas the latter (as far as I'm aware) does "whatever Microsoft Visual C++ does".
A few things to note here:
- In the Clang/P2996 fork, I at some point basically stopped updating
MicrosoftMangle.cpp(for no other principled reason than laziness). - Since
MicrosoftMangle.cppattempts to remain compatible with "whatever Microsoft Visual C++" does, any mangling implemented here is just guesswork (which will probably be wrong) until Microsoft implements P2996 (which will surely any day now, right? 😉) Until then, it would probably be appropriate to just issue a diagnostic and fail the build if we try to mangle a reflection with Microsoft mangling. - Please note that there is an active Github issue on the Itanium ABI repository discussing mangling for reflections. Anything mangling implemented in
ItaniumMangle.cppshould prefer to follow what's suggested there, rather than follow whatever random nonsense I did in the Clang/P2996 repository. If any issues are encountered, we should of course surface them on that discussion thread.
@AaronBallman - Does that all sound right to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronBallman - Does that all sound right to you?
That all sounds correct to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronBallman after looking more into this, I think we can start working on this in subsequent PRs when more facilities are available. For now I'm trying to keep the scope of this PR small to get it merged. Does that sound good to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof, doing this as a 'fallthrough' is probably a mistake. We should do this as a Diags.Report (we have one for 'we dont know what to do wtih this yet`.
We should just error-out, like we do in the !NullOut situation below, but do so always.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make CXXReflectExprClass join the !NullOut situation below for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably just not do a fallthrough, and just copy/paste the stuff inside the !NullOpt into it.
Co-authored-by: Aaron Ballman <[email protected]>
🐧 Linux x64 Test Results
|
|
After an internal discussion with @katzdm and @cor3ntin , we are thinking of reducing the scope of this PR to only parse primitive types for now. Also, the design of |
A little more detail - this is a sketch I shared of the design that we discussed moving towards: class CXXReflectExpr : public Expr {
enum class OperandKind {
Type, // TypeLoc
Namespace, // NamespaceReference
Template, // TemplateReference
IdExpr, // DeclRefExpr
};
SourceLocation OpLoc; // Location of '^^'
OperandKind Kind;
const void *OperandRef;
CXXReflectExpr(SourceLocation CaretCaret, const TypeLoc *TL)
: OpLoc(CaretCaret), OperandRef(TL), Kind(OperandKind::Type) {}
CXXReflectExpr(SourceLocation CaretCaret, const NamespaceReference *NSRef)
: OpLoc(CaretCaret), OperandRef(NSRef), Kind(OperandKind::Namespace)
// ... etc ...
SourceLocation getBeginLoc() const {
switch (Kind) {
case OperandKind::Type:
return static_cast<const TypeLoc *>(OperandRef)->getBeginLoc();
case OperandKind::Namespace:
return static_cast<const NamespaceReference *>(OperandRef)->getBeginLoc();
case OperandKind::Template:
return static_cast<const TemplateReference *>(OperandRef)->getBeginLoc();
case OperandKind::IdExpr:
return static_cast<const DeclRefExpr *>(OperandRef)->getBeginLoc();
default:
llvm_unreachable("unknown operand kind");
}
}
// ...
};where The PR following this one would integrate parsing and representation of other reflect-expressions whose operands are types, followed by additional PRs that will handle the other three cases. |
This seems reasonable to me. Instead of a |
I had originally thought of using |
I have a slightly-more-than-mild preference for something like PointerUnion, as it better expresses the types it contains/won't allow us to do silly things in there/keeps the type and discriminator in sync in a way that is otherwise unfortunately manual. IIRC there is a |
Yeah, I was also going to suggest using |
|
Since we removed parsing global namespace in this PR, I also removed tests related to namespace, and just keep the tests related to primitive types. We can add tests for the remaining items in the next PR, and also improve the diagnostics message. |
erichkeane
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should be using llvm_unreachable for not implemented, we should diagnose whenever possible, but unreachable is an opt-hint. Though, assert(false...) will only apply during asserts builds, it at least doesn't result in UB.
We do this a few times in this patch.
Else, a couple of suggestions.
|
|
||
| let CategoryName = "Reflection Issue" in { | ||
| def err_cannot_reflect_operand : Error< | ||
| "expected reflectable entity">; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its probably ok to leave this really awful for now, I have no idea what it means either,, but I'm hopeful this'll improve.
DEPENDING on how long we intend this to last, we might think about saying something like, unknown or unimplemented reflectable entity.
| E = cast<ConstantExpr>(E)->getSubExpr(); | ||
| goto recurse; | ||
|
|
||
| case Expr::CXXReflectExprClass: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof, doing this as a 'fallthrough' is probably a mistake. We should do this as a Diags.Report (we have one for 'we dont know what to do wtih this yet`.
We should just error-out, like we do in the !NullOut situation below, but do so always.
| // operator and the second being the introducer for the block. | ||
| if (OpToken.is(tok::caretcaret)) { | ||
| assert(getLangOpts().Reflection && | ||
| "reflection support disabled - compile with -freflection"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
messages to users in 'asserts' is weird. If we want to say something like this, we should be diagnosing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be a diagnostic - this is more of a "sanity check", since tok::caretcaret should never be lexed unless getLangOpts().Reflection is enabled.
In light of that, maybe change the assert-message. Something like:
"token '^^' can only appear with reflection enabled"
Or just omit the message.
assert(getLangOpts().Reflection);
Co-authored-by: Erich Keane <[email protected]>
Co-authored-by: Erich Keane <[email protected]>
(After changing the scope) This PR implements parsing the reflection operator (^^) for primitive types. The goal is to keep the first PR simple. In subsequent PRs, parsing for the remaining requirements will be introduced.
This implementation is based on the fork of @katzdm.
Class
CXXReflectExpris introduced to represent the operand of the reflection operator. For now, in this PR, the type std::meta::info is not implemented yet, so when we construct an AST node CXXReflectExpr,VoidTyis used as placeholder type for now.The file
ParseReflect.cppis introduced, which for now only has the functionParseCXXReflectExpression. It parses the operand of the reflection operator.