Author: Nhat Nguyen Date: 2026-02-06T11:32:17-05:00 New Revision: 1171450d56e22b32894863c894a487a5813bd5a6
URL: https://github.com/llvm/llvm-project/commit/1171450d56e22b32894863c894a487a5813bd5a6 DIFF: https://github.com/llvm/llvm-project/commit/1171450d56e22b32894863c894a487a5813bd5a6.diff LOG: [clang]: reflection operator parsing for primitive types (#164692) (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 `CXXReflectExpr` is 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, `VoidTy` is used as placeholder type for now. The file `ParseReflect.cpp` is introduced, which for now only has the function `ParseCXXReflectExpression`. It parses the operand of the reflection operator. --------- Co-authored-by: Shafik Yaghmour <[email protected]> Co-authored-by: Hubert Tong <[email protected]> Co-authored-by: Sirraide <[email protected]> Co-authored-by: Aaron Ballman <[email protected]> Co-authored-by: Erich Keane <[email protected]> Added: clang/lib/Parse/ParseReflect.cpp clang/test/CodeGenCXX/reflection-mangle-itanium.cpp clang/test/CodeGenCXX/reflection-mangle-ms.cpp clang/test/Driver/reflection-requires-cxx26.cpp clang/test/Parser/parsing-reflection-with-blocks.cpp clang/test/Parser/parsing-reflection.cpp Modified: clang/include/clang/AST/ExprCXX.h clang/include/clang/AST/RecursiveASTVisitor.h clang/include/clang/Basic/DiagnosticDriverKinds.td clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Basic/LangOptions.def clang/include/clang/Basic/StmtNodes.td clang/include/clang/Basic/TokenKinds.def clang/include/clang/Options/Options.td clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Sema.h clang/include/clang/Serialization/ASTBitCodes.h clang/lib/AST/Expr.cpp clang/lib/AST/ExprCXX.cpp clang/lib/AST/ExprClassification.cpp clang/lib/AST/ExprConstant.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/AST/StmtPrinter.cpp clang/lib/AST/StmtProfile.cpp clang/lib/Basic/OperatorPrecedence.cpp clang/lib/Frontend/CompilerInvocation.cpp clang/lib/Lex/Lexer.cpp clang/lib/Parse/CMakeLists.txt clang/lib/Parse/ParseExpr.cpp clang/lib/Parse/ParseTentative.cpp clang/lib/Sema/SemaExceptionSpec.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/TreeTransform.h clang/lib/Serialization/ASTReaderStmt.cpp clang/lib/Serialization/ASTWriterStmt.cpp clang/lib/StaticAnalyzer/Core/ExprEngine.cpp clang/tools/libclang/CXCursor.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 9435ab069a520..c40cd929b7408 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -44,6 +44,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/TypeSwitch.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" @@ -5499,6 +5500,60 @@ class BuiltinBitCastExpr final } }; +/// Represents a C++26 reflect expression [expr.reflect]. The operand of the +/// expression is either: +/// - :: (global namespace), +/// - a reflection-name, +/// - a type-id, or +/// - an id-expression. +class CXXReflectExpr : public Expr { + + // TODO(Reflection): add support for TemplateReference, NamespaceReference and + // DeclRefExpr + using operand_type = llvm::PointerUnion<const TypeSourceInfo *>; + + SourceLocation CaretCaretLoc; + operand_type Operand; + + CXXReflectExpr(SourceLocation CaretCaretLoc, const TypeSourceInfo *TSI); + CXXReflectExpr(EmptyShell Empty); + +public: + static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, + TypeSourceInfo *TL); + + static CXXReflectExpr *CreateEmpty(ASTContext &C); + + SourceLocation getBeginLoc() const LLVM_READONLY { + return llvm::TypeSwitch<operand_type, SourceLocation>(Operand) + .Case<const TypeSourceInfo *>( + [](auto *Ptr) { return Ptr->getTypeLoc().getBeginLoc(); }); + } + + SourceLocation getEndLoc() const LLVM_READONLY { + return llvm::TypeSwitch<operand_type, SourceLocation>(Operand) + .Case<const TypeSourceInfo *>( + [](auto *Ptr) { return Ptr->getTypeLoc().getEndLoc(); }); + } + + /// Returns location of the '^^'-operator. + SourceLocation getOperatorLoc() const { return CaretCaretLoc; } + + child_range children() { + // TODO(Reflection) + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + // TODO(Reflection) + 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 9e18752145aef..3283ab664ad38 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2884,6 +2884,8 @@ 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/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 6951f6f22ccbf..90a92b1602231 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -919,4 +919,7 @@ def warn_drv_gcc_install_dir_libstdcxx : Warning< "future releases of the clang compiler will prefer GCC installations " "containing libstdc++ include directories; '%0' would be chosen over '%1'">, InGroup<DiagGroup<"gcc-install-dir-libstdcxx">>; + +def err_drv_reflection_requires_cxx26 : Error< + "option '%0' is only supported when compiling in C++26 mode">; } diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 457d3644de35a..de10dbe5d0628 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1847,6 +1847,11 @@ def err_placeholder_expected_auto_or_decltype_auto : Error< "expected 'auto' or 'decltype(auto)' after concept name">; } +let CategoryName = "Reflection Diagnostics" in { +def err_cannot_reflect_operand : Error< + "unknown or unimplemented reflectable entity">; +} + 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/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index ba12e522f331f..45e2777def4fa 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -510,6 +510,7 @@ LANGOPT(EnableLifetimeSafetyInference, 1, 0, NotCompatible, "Lifetime safety inf LANGOPT(EnableLifetimeSafetyTUAnalysis, 1, 0, NotCompatible, "Lifetime safety at translation-unit end, analyzing functions in call graph post-order 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 b08b9fe3b9271..cc928e2ea42c4 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -179,6 +179,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 4ee4aa8b31f65..84339c8b64db9 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -238,6 +238,7 @@ PUNCTUATOR(greatergreater, ">>") PUNCTUATOR(greaterequal, ">=") PUNCTUATOR(greatergreaterequal, ">>=") PUNCTUATOR(caret, "^") +PUNCTUATOR(caretcaret, "^^") PUNCTUATOR(caretequal, "^=") PUNCTUATOR(pipe, "|") PUNCTUATOR(pipepipe, "||") diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 3e3239bef677e..54e162c262a90 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -3784,6 +3784,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, [], [CC1Option], + "Enable C++26 reflection">, + NegFlag<SetFalse>>, ShouldParseIf<cplusplus.KeyPath>; defm sized_deallocation : BoolFOption<"sized-deallocation", LangOpts<"SizedDeallocation">, Default<cpp14.KeyPath>, PosFlag<SetTrue, [], [], "Enable C++14 sized global deallocation functions">, diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 2b1dcabc9a194..5ae02e2b4e8ad 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. @@ -5152,6 +5153,15 @@ class Parser : public CodeCompletionHandler { ///@} + //===--------------------------------------------------------------------===// + // Reflection parsing + + /// ParseCXXReflectExpression - parses the operand of reflection operator. + /// + /// \returns on success, an expression holding the constructed CXXReflectExpr; + /// on failure, an ExprError. + ExprResult ParseCXXReflectExpression(); + // // // ------------------------------------------------------------------------- diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0ba3daab764b7..fe4616d89df89 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -14838,6 +14838,12 @@ class Sema final : public SemaBase { /// Implementations are in SemaConcept.cpp ///@{ +public: + ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, TypeSourceInfo *TSI); + + ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, + TypeSourceInfo *TSI); + 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 5a86d540e5d0b..1bf96e6bbc793 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1928,6 +1928,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/Expr.cpp b/clang/lib/AST/Expr.cpp index 4bb979e51b75d..52e08686589b0 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3734,6 +3734,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case PackIndexingExprClass: case HLSLOutArgExprClass: case OpenACCAsteriskSizeExprClass: + case CXXReflectExprClass: // These never have a side-effect. return false; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index c7f0ff040194d..bcc481fc8399f 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1939,6 +1939,24 @@ TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C, return new (Mem) TypeTraitExpr(EmptyShell(), IsStoredAsBool); } +CXXReflectExpr::CXXReflectExpr(EmptyShell Empty) + : Expr(CXXReflectExprClass, Empty) {} + +CXXReflectExpr::CXXReflectExpr(SourceLocation CaretCaretLoc, + const TypeSourceInfo *TSI) + : Expr(CXXReflectExprClass, TSI->getType(), VK_PRValue, OK_Ordinary), + CaretCaretLoc(CaretCaretLoc), Operand(TSI) {} + +CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, + SourceLocation CaretCaretLoc, + TypeSourceInfo *TSI) { + return new (C) CXXReflectExpr(CaretCaretLoc, TSI); +} + +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 9995d1b411c5b..177507240e98e 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 ff433ed729a28..05a0746b69969 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -21117,6 +21117,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 fa28c0d444cc4..938ae75ea1eb6 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4951,6 +4951,11 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, E = cast<ConstantExpr>(E)->getSubExpr(); goto recurse; + case Expr::CXXReflectExprClass: { + // TODO(Reflection): implement this after introducing std::meta::info + assert(false && "unimplemented"); + } + // 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 4d1ad387b8e8d..f0aa284aa7e37 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2579,6 +2579,11 @@ void StmtPrinter::VisitCXXUnresolvedConstructExpr( OS << ')'; } +void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) { + // TODO(Reflection): Implement this. + assert(false && "not implemented yet"); +} + void StmtPrinter::VisitCXXDependentScopeMemberExpr( CXXDependentScopeMemberExpr *Node) { if (!Node->isImplicitAccess()) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index efabe9809c361..7a61b1f00c6f3 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2188,6 +2188,11 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) { ID.AddInteger(Hasher.CalculateHash()); } +void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) { + // TODO(Reflection): Implement this. + assert(false && "not implemented yet"); +} + void StmtProfiler::VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *S) { VisitExpr(S); diff --git a/clang/lib/Basic/OperatorPrecedence.cpp b/clang/lib/Basic/OperatorPrecedence.cpp index c4e8fe96cdf4b..f328d1efa2f87 100644 --- a/clang/lib/Basic/OperatorPrecedence.cpp +++ b/clang/lib/Basic/OperatorPrecedence.cpp @@ -54,6 +54,10 @@ prec::Level getBinOpPrecedence(tok::TokenKind Kind, bool GreaterThanIsOperator, case tok::pipepipe: return prec::LogicalOr; case tok::ampamp: return prec::LogicalAnd; case tok::pipe: return prec::InclusiveOr; + // this is for the case when ^^ appears where a binary operator is needed, + // and the first ^ is the actual binary operator, + // and the second is for a block. + case tok::caretcaret: case tok::caret: return prec::ExclusiveOr; case tok::amp: return prec::And; case tok::exclaimequal: diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 2af4a7f536623..6aa2afb6f5918 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -622,6 +622,11 @@ static bool FixupInvocation(CompilerInvocation &Invocation, LangOpts.RawStringLiterals = true; } + if (Args.hasArg(OPT_freflection) && !LangOpts.CPlusPlus26) { + Diags.Report(diag::err_drv_reflection_requires_cxx26) + << Args.getLastArg(options::OPT_freflection)->getAsString(Args); + } + LangOpts.NamedLoops = Args.hasFlag(OPT_fnamed_loops, OPT_fno_named_loops, LangOpts.C2y); diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 5e9d2743ba53f..1498657047bd6 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -4374,6 +4374,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..be6c7824cdbae 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -333,6 +333,27 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { Token OpToken = Tok; ConsumeToken(); + // The reflection operator is not valid here (i.e., in the place of the + // operator token in a binary expression), so if reflection and blocks are + // enabled, we split caretcaret into two carets: the first being the binary + // operator and the second being the introducer for the block. + if (OpToken.is(tok::caretcaret)) { + assert(getLangOpts().Reflection); + if (getLangOpts().Blocks) { + OpToken.setKind(tok::caret); + Token Caret; + { + Caret.startToken(); + Caret.setKind(tok::caret); + Caret.setLocation(OpToken.getLocation().getLocWithOffset(1)); + Caret.setLength(1); + } + UnconsumeToken(OpToken); + PP.EnterToken(Caret, /*IsReinject=*/true); + return ParseRHSOfBinaryExpression(LHS, MinPrec); + } + } + // If we're potentially in a template-id, we may now be able to determine // whether we're actually in one or not. if (OpToken.isOneOf(tok::comma, tok::greater, tok::greatergreater, @@ -1208,6 +1229,18 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand, AllowSuffix = false; Res = ParseUnaryExprOrTypeTraitExpression(); break; + case tok::caretcaret: { + if (!getLangOpts().Reflection) { + NotCastExpr = true; + return ExprError(); + } + + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + AllowSuffix = false; + Res = ParseCXXReflectExpression(); + break; + } case tok::ampamp: { // unary-expression: '&&' identifier if (NotPrimaryExpression) *NotPrimaryExpression = true; diff --git a/clang/lib/Parse/ParseReflect.cpp b/clang/lib/Parse/ParseReflect.cpp new file mode 100644 index 0000000000000..b3914a3701a6d --- /dev/null +++ b/clang/lib/Parse/ParseReflect.cpp @@ -0,0 +1,52 @@ +//===--- ParseReflect.cpp - C++26 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() { + // TODO(reflection) : support parsing for global namespace, + // reflection-name, id-expression and remaining supports for + // type-id (placeholder type, alias template, etc.) + EnterExpressionEvaluationContext Unevaluated( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + assert(Tok.is(tok::caretcaret)); + SourceLocation CaretCaretLoc = ConsumeToken(); + SourceLocation OperandLoc = Tok.getLocation(); + + if (isCXXTypeId(TentativeCXXTypeIdContext::AsReflectionOperand)) { + TypeResult TR = ParseTypeName(/*TypeOf=*/nullptr); + if (TR.isInvalid()) + return ExprError(); + + TypeSourceInfo *TSI = nullptr; + QualType QT = Actions.GetTypeFromParser(TR.get(), &TSI); + + if (QT.isNull()) + return ExprError(); + + if (!TSI) + TSI = Actions.getASTContext().getTrivialTypeSourceInfo(QT, OperandLoc); + + QT = QT.getCanonicalType().getUnqualifiedType(); + if (TSI && QT.getTypePtr()->isBuiltinType()) { + // Only supports builtin types for now + return Actions.ActOnCXXReflectExpr(CaretCaretLoc, 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 9622a00687ca5..34b86db793890 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 6208d6679df73..ebf7920bd8b0d 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1380,6 +1380,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 5795a71b5cae8..27f1cce88897b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17922,6 +17922,16 @@ void Sema::PushExpressionEvaluationContextForFunction( } } +ExprResult Sema::ActOnCXXReflectExpr(SourceLocation CaretCaretLoc, + TypeSourceInfo *TSI) { + return BuildCXXReflectExpr(CaretCaretLoc, TSI); +} + +ExprResult Sema::BuildCXXReflectExpr(SourceLocation CaretCaretLoc, + TypeSourceInfo *TSI) { + return CXXReflectExpr::Create(Context, CaretCaretLoc, TSI); +} + namespace { const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index fb32b0e70e3c9..9e0980af6c06b 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13054,6 +13054,12 @@ ExprResult TreeTransform<Derived>::TransformSYCLUniqueStableNameExpr( E->getLocation(), E->getLParenLocation(), E->getRParenLocation(), NewT); } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformCXXReflectExpr(CXXReflectExpr *E) { + // TODO(reflection): Implement its transform + assert(false && "not implemented yet"); +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformPredefinedExpr(PredefinedExpr *E) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 5553139dfaa46..1606615586055 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -535,6 +535,11 @@ void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) { } } +void ASTStmtReader::VisitCXXReflectExpr(CXXReflectExpr *E) { + // TODO(Reflection): Implement this. + assert(false && "not implemented yet"); +} + void ASTStmtReader::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) { VisitStmt(S); S->setOriginalStmt(cast<CompoundStmt>(Record.readSubStmt())); @@ -4542,6 +4547,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_HLSL_OUT_ARG: S = HLSLOutArgExpr::CreateEmpty(Context); break; + case EXPR_REFLECT: { + S = CXXReflectExpr::CreateEmpty(Context); + break; + } } // We hit a STMT_STOP, so we're done with this expression. diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 8f22156f93487..ae2a0d503b629 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -473,6 +473,11 @@ void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *E) { Code = serialization::EXPR_COYIELD; } +void ASTStmtWriter::VisitCXXReflectExpr(CXXReflectExpr *E) { + // TODO(Reflection): Implement this. + assert(false && "not implemented yet"); +} + void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getKeywordLoc()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1249fe6423ccb..2af5108005ac4 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1726,6 +1726,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, switch (S->getStmtClass()) { // C++, OpenMP and ARC stuff we don't support yet. case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::CXXReflectExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: diff --git a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp new file mode 100644 index 0000000000000..653bf7aebd876 --- /dev/null +++ b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -std=c++26 -freflection -triple x86_64-unknown-linux-gnu \ +// RUN: -emit-llvm -o - %s -verify + +int main() { + (void)(^^int); // expected-error {{cannot compile this scalar expression yet}} +} diff --git a/clang/test/CodeGenCXX/reflection-mangle-ms.cpp b/clang/test/CodeGenCXX/reflection-mangle-ms.cpp new file mode 100644 index 0000000000000..6dd8aefbcec96 --- /dev/null +++ b/clang/test/CodeGenCXX/reflection-mangle-ms.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -std=c++26 -freflection -triple x86_64-pc-windows-msvc \ +// RUN: -emit-llvm -o - %s -verify + +int main() { + (void)(^^int); // expected-error {{cannot compile this scalar expression yet}} +} diff --git a/clang/test/Driver/reflection-requires-cxx26.cpp b/clang/test/Driver/reflection-requires-cxx26.cpp new file mode 100644 index 0000000000000..276b3c478c9ac --- /dev/null +++ b/clang/test/Driver/reflection-requires-cxx26.cpp @@ -0,0 +1,3 @@ +// RUN: not %clangxx -fsyntax-only -std=c++23 -Xclang -freflection %s 2>&1 | FileCheck %s +// +// CHECK: error: option '-freflection' is only supported when compiling in C++26 mode diff --git a/clang/test/Parser/parsing-reflection-with-blocks.cpp b/clang/test/Parser/parsing-reflection-with-blocks.cpp new file mode 100644 index 0000000000000..6db7fff1dc030 --- /dev/null +++ b/clang/test/Parser/parsing-reflection-with-blocks.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 %s -std=c++26 -freflection -fblocks -fsyntax-only + +struct A { + int operator^(int (^block)(int x)) const { + return block(0); + } +}; + + +consteval void test() +{ + (void)(A{}^^(int y){ return y + 1; }); + (void)(1^^(){ return 1; }()); + (void)(1^^{ return 1; }()); + + { + (void)(^^int); + (void)(A{}^^(int y){ return y + 1; }); + } +} diff --git a/clang/test/Parser/parsing-reflection.cpp b/clang/test/Parser/parsing-reflection.cpp new file mode 100644 index 0000000000000..4eabe7d8683fe --- /dev/null +++ b/clang/test/Parser/parsing-reflection.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only -verify + +struct A{}; +namespace B{}; +void f(){}; + +consteval void test() +{ + (void)(^^void); + (void)(^^bool); + (void)(^^char); + (void)(^^signed char); + (void)(^^unsigned char); + (void)(^^short); + (void)(^^unsigned short); + (void)(^^int); + (void)(^^unsigned int); + (void)(^^long); + (void)(^^unsigned long); + (void)(^^long long); + (void)(^^float); + (void)(^^double); + (void)(^^const void); + (void)(^^decltype(nullptr)); + + (void)(^^::); // expected-error {{unknown or unimplemented reflectable entity}} + constexpr auto x = 1; + (void)(^^x); // expected-error {{unknown or unimplemented reflectable entity}} + (void)(^^A); // expected-error {{unknown or unimplemented reflectable entity}} + (void)(^^B); // expected-error {{unknown or unimplemented reflectable entity}} + (void)(^^f); // expected-error {{unknown or unimplemented reflectable entity}} +} diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 08ea73dcded08..352b5c0575868 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -310,6 +310,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: case Stmt::CXXFoldExprClass: + case Stmt::CXXReflectExprClass: case Stmt::CXXRewrittenBinaryOperatorClass: case Stmt::CXXStdInitializerListExprClass: case Stmt::CXXScalarValueInitExprClass: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
