https://github.com/changkhothuychung updated https://github.com/llvm/llvm-project/pull/190356
>From fbcf8beba1c326496c75735e68ae0adb4d55c48d Mon Sep 17 00:00:00 2001 From: changkhothuychung <[email protected]> Date: Tue, 28 Jul 2026 11:05:30 -0400 Subject: [PATCH] support std::meta::info for primitive types --- clang/include/clang/AST/APValue.h | 45 +++++++++- clang/include/clang/AST/ASTContext.h | 1 + clang/include/clang/AST/BuiltinTypes.def | 3 + clang/include/clang/AST/ExprCXX.h | 21 +++-- clang/include/clang/AST/PropertiesBase.td | 25 ++++++ clang/include/clang/AST/Reflection.h | 64 ++++++++++++++ clang/include/clang/AST/TypeBase.h | 5 ++ clang/include/clang/Basic/TargetInfo.h | 6 ++ .../include/clang/Serialization/ASTBitCodes.h | 5 +- clang/lib/AST/APValue.cpp | 55 ++++++++++++ clang/lib/AST/ASTContext.cpp | 9 ++ clang/lib/AST/ASTImporter.cpp | 34 +++++++- clang/lib/AST/ByteCode/Compiler.cpp | 19 +++++ clang/lib/AST/ByteCode/Compiler.h | 1 + clang/lib/AST/ByteCode/Context.cpp | 3 + clang/lib/AST/ByteCode/Descriptor.cpp | 1 + clang/lib/AST/ByteCode/Disasm.cpp | 2 + clang/lib/AST/ByteCode/Interp.cpp | 2 +- clang/lib/AST/ByteCode/Interp.h | 7 ++ .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 2 +- clang/lib/AST/ByteCode/InterpStack.h | 3 + clang/lib/AST/ByteCode/InterpState.cpp | 1 + clang/lib/AST/ByteCode/Opcodes.td | 9 +- clang/lib/AST/ByteCode/PrimType.cpp | 1 + clang/lib/AST/ByteCode/PrimType.h | 6 ++ clang/lib/AST/ByteCode/Program.cpp | 1 + clang/lib/AST/ByteCode/Program.h | 1 + clang/lib/AST/ByteCode/Reflect.h | 60 ++++++++++++++ clang/lib/AST/ExprCXX.cpp | 11 +-- clang/lib/AST/ExprConstant.cpp | 77 ++++++++++++++++- clang/lib/AST/ItaniumMangle.cpp | 83 ++++++++++++++++++- clang/lib/AST/MicrosoftMangle.cpp | 5 ++ clang/lib/AST/NSAPI.cpp | 1 + clang/lib/AST/StmtPrinter.cpp | 11 ++- clang/lib/AST/StmtProfile.cpp | 24 +++++- clang/lib/AST/TextNodeDumper.cpp | 16 ++++ clang/lib/AST/Type.cpp | 10 +++ clang/lib/AST/TypeLoc.cpp | 1 + clang/lib/Basic/TargetInfo.cpp | 2 + clang/lib/CodeGen/CGDebugInfo.cpp | 2 + clang/lib/CodeGen/CGExprConstant.cpp | 2 + clang/lib/CodeGen/CodeGenModule.cpp | 2 + clang/lib/CodeGen/CodeGenTypes.cpp | 7 ++ clang/lib/CodeGen/ItaniumCXXABI.cpp | 1 + clang/lib/CodeGen/QualTypeMapper.cpp | 3 + clang/lib/Sema/SemaExpr.cpp | 13 ++- clang/lib/Sema/SemaOverload.cpp | 23 +++-- clang/lib/Sema/SemaTemplate.cpp | 17 +++- clang/lib/Sema/TreeTransform.h | 14 +++- clang/lib/Serialization/ASTCommon.cpp | 3 + clang/lib/Serialization/ASTReader.cpp | 3 + clang/lib/Serialization/ASTReaderStmt.cpp | 15 +++- clang/lib/Serialization/ASTWriterStmt.cpp | 15 +++- .../UnifiedSymbolResolution/USRGeneration.cpp | 3 + .../test/AST/ast-dump-APValue-reflection.cpp | 21 +++++ .../reflection-emit-meta-info-itanium.cpp | 7 ++ ...s.cpp => reflection-emit-meta-info-ms.cpp} | 0 .../CodeGenCXX/reflection-mangle-itanium.cpp | 45 +++++++++- clang/test/PCH/reflection.cpp | 14 ++++ clang/test/PCH/reflection_include.h | 1 + clang/test/Sema/reflection-meta-info.fail.cpp | 56 +++++++++++++ clang/test/Sema/reflection-meta-info.pass.cpp | 77 +++++++++++++++++ clang/tools/libclang/CIndex.cpp | 1 + clang/unittests/AST/ASTImporterTest.cpp | 29 +++++++ .../TypeSystem/Clang/TypeSystemClang.cpp | 3 + 65 files changed, 969 insertions(+), 41 deletions(-) create mode 100644 clang/include/clang/AST/Reflection.h create mode 100644 clang/lib/AST/ByteCode/Reflect.h create mode 100644 clang/test/AST/ast-dump-APValue-reflection.cpp create mode 100644 clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp rename clang/test/CodeGenCXX/{reflection-mangle-ms.cpp => reflection-emit-meta-info-ms.cpp} (100%) create mode 100644 clang/test/PCH/reflection.cpp create mode 100644 clang/test/PCH/reflection_include.h create mode 100644 clang/test/Sema/reflection-meta-info.fail.cpp create mode 100644 clang/test/Sema/reflection-meta-info.pass.cpp diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index 9293266a41256..3308b01e5bcad 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_APVALUE_H #define LLVM_CLANG_AST_APVALUE_H +#include "clang/AST/Reflection.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/APFloat.h" @@ -141,7 +142,8 @@ class APValue { Struct, Union, MemberPointer, - AddrLabelDiff + AddrLabelDiff, + Reflection }; class alignas(uint64_t) LValueBase { @@ -315,12 +317,23 @@ class APValue { const AddrLabelExpr* LHSExpr; const AddrLabelExpr* RHSExpr; }; + struct ReflectionData { + // OperandKind will eventually have support for + // Null, TypeSourceInfo, TemplateReference, NamespaceReference, DeclRefExpr. + // Operand stores the opaque pointer of the reflection operand. + // Depending on the value of OperandKind, we can perform the + // corresponding cast to the associated type. + // If OperandKind is Null, then the ReflectionData represents + // a null reflection, and therefore Operand should be a nullptr. + ReflectionKind OperandKind; + const void *Operand; + }; struct MemberPointerData; // We ensure elsewhere that Data is big enough for LV and MemberPointerData. - typedef llvm::AlignedCharArrayUnion<void *, APSInt, APFloat, ComplexAPSInt, - ComplexAPFloat, Vec, Mat, Arr, StructData, - UnionData, AddrLabelDiffData> + typedef llvm::AlignedCharArrayUnion< + void *, APSInt, APFloat, ComplexAPSInt, ComplexAPFloat, Vec, Mat, Arr, + StructData, UnionData, AddrLabelDiffData, ReflectionData> DataType; static const size_t DataSize = sizeof(DataType); @@ -415,6 +428,14 @@ class APValue { : Kind(None), AllowConstexprUnknown(false) { MakeArray(InitElts, Size); } + + /// Creates a new Reflection APValue. + /// \param OperandKind The kind of reflection. + /// \param Operand The entity being reflected. + APValue(ReflectionKind OperandKind, const void *Operand) : Kind(None) { + MakeReflection(OperandKind, Operand); + } + /// Creates a new struct APValue. /// \param UninitStruct Marker. Pass an empty UninitStruct. /// \param NumBases Number of bases. @@ -498,6 +519,7 @@ class APValue { bool isUnion() const { return Kind == Union; } bool isMemberPointer() const { return Kind == MemberPointer; } bool isAddrLabelDiff() const { return Kind == AddrLabelDiff; } + bool isReflection() const { return Kind == Reflection; } void dump() const; void dump(raw_ostream &OS, const ASTContext &Context) const; @@ -717,6 +739,16 @@ class APValue { return ((const AddrLabelDiffData *)(const char *)&Data)->RHSExpr; } + ReflectionKind getReflectionOperandKind() const { + assert(isReflection() && "Invalid accessor"); + return ((const ReflectionData *)(const char *)&Data)->OperandKind; + } + + const void *getReflectionOpaqueOperand() const { + assert(isReflection() && "Invalid accessor"); + return ((const ReflectionData *)(const char *)&Data)->Operand; + } + void setInt(APSInt I) { assert(isInt() && "Invalid accessor"); *(APSInt *)(char *)&Data = std::move(I); @@ -767,6 +799,11 @@ class APValue { private: void DestroyDataAndMakeUninit(); + void MakeReflection(ReflectionKind OperandKind, const void *Operand) { + assert(isAbsent() && "Bad state change"); + new ((void *)(char *)Data.buffer) ReflectionData{OperandKind, Operand}; + Kind = Reflection; + } void MakeInt() { assert(isAbsent() && "Bad state change"); new ((void *)&Data) APSInt(1); diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 7ed6509c3c16c..05dfec8c37586 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1351,6 +1351,7 @@ class ASTContext : public RefCountedBase<ASTContext> { CanQualType BFloat16Ty; CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3 CanQualType VoidPtrTy, NullPtrTy; + CanQualType MetaInfoTy; CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy, UnknownAnyTy; CanQualType BuiltinFnTy; diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def index 444be4311a743..ee2f3f0365126 100644 --- a/clang/include/clang/AST/BuiltinTypes.def +++ b/clang/include/clang/AST/BuiltinTypes.def @@ -223,6 +223,9 @@ FLOATING_TYPE(Ibm128, Ibm128Ty) //===- Language-specific types --------------------------------------------===// +// 'std::meta::info' in C++ +BUILTIN_TYPE(MetaInfo, MetaInfoTy) + // This is the type of C++0x 'nullptr'. BUILTIN_TYPE(NullPtr, NullPtrTy) diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 757f2137c90dc..4ed388877a2ab 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -25,6 +25,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/Reflection.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/TemplateBase.h" @@ -5502,37 +5503,47 @@ class BuiltinBitCastExpr final /// - a type-id, or /// - an id-expression. class CXXReflectExpr : public Expr { + friend class ASTStmtReader; + friend class ASTStmtWriter; +private: // TODO(Reflection): add support for TemplateReference, NamespaceReference and // DeclRefExpr - using operand_type = llvm::PointerUnion<const TypeSourceInfo *>; + using operand_type = llvm::PointerUnion<TypeSourceInfo *>; SourceLocation CaretCaretLoc; + ReflectionKind Kind; operand_type Operand; - CXXReflectExpr(SourceLocation CaretCaretLoc, const TypeSourceInfo *TSI); + CXXReflectExpr(ASTContext &C, SourceLocation CaretCaretLoc, + TypeSourceInfo *TSI); CXXReflectExpr(EmptyShell Empty); public: static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, - TypeSourceInfo *TL); + TypeSourceInfo *TSI); static CXXReflectExpr *CreateEmpty(ASTContext &C); SourceLocation getBeginLoc() const LLVM_READONLY { return llvm::TypeSwitch<operand_type, SourceLocation>(Operand) - .Case<const TypeSourceInfo *>( + .Case<TypeSourceInfo *>( [](auto *Ptr) { return Ptr->getTypeLoc().getBeginLoc(); }); } SourceLocation getEndLoc() const LLVM_READONLY { return llvm::TypeSwitch<operand_type, SourceLocation>(Operand) - .Case<const TypeSourceInfo *>( + .Case<TypeSourceInfo *>( [](auto *Ptr) { return Ptr->getTypeLoc().getEndLoc(); }); } /// Returns location of the '^^'-operator. SourceLocation getOperatorLoc() const { return CaretCaretLoc; } + ReflectionKind getKind() const { return Kind; } + void *getOpaqueValue() const { return Operand.getOpaqueValue(); } + TypeSourceInfo *getTypeSourceInfo() const { + return cast<TypeSourceInfo *>(Operand); + } child_range children() { // TODO(Reflection) diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 25ef4c26a9aa1..9a5c683fbf693 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -603,6 +603,31 @@ let Class = PropertyTypeCase<APValue, "LValue"> in { }]>; } +def ReflectionKind : EnumPropertyType<"ReflectionKind">; + +let Class = PropertyTypeCase<APValue, "Reflection"> in { + def : Property<"reflectionKind", ReflectionKind> { + let Read = [{ node.getReflectionOperandKind() }]; + } + def : Property<"reflectionType", QualType> { + let Conditional = [{ reflectionKind == ReflectionKind::Type }]; + let Read = [{ + QualType::getFromOpaquePtr(node.getReflectionOpaqueOperand()) + }]; + } + def : Creator<[{ + switch (reflectionKind) { + // TODO(Reflection): Add support for TypeSourceInfo, NamespaceReference + // TemplateReference and DeclRefExpr + case ReflectionKind::Null: + return APValue(reflectionKind, nullptr); + case ReflectionKind::Type: + return APValue(reflectionKind, (*reflectionType).getAsOpaquePtr()); + } + llvm_unreachable("unimplemented or unknow reflection entities"); + }]>; +} + // Type cases for DeclarationName. def : PropertyTypeKind<DeclarationName, DeclarationNameKind, "node.getNameKind()">; diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h new file mode 100644 index 0000000000000..2deffae7f8112 --- /dev/null +++ b/clang/include/clang/AST/Reflection.h @@ -0,0 +1,64 @@ +//===--- Reflection.h - Kind of reflection operands ---*- C++ -*-===// +// +// 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 declares the kinds of reflection operands. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_REFLECTION_H +#define LLVM_CLANG_AST_REFLECTION_H + +#include "clang/AST/TypeBase.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { + +// TODO(Reflection): Add support for Template, Namespace and DeclRefExpr. +enum class ReflectionKind { Null, Type }; + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + ReflectionKind Kind) { + switch (Kind) { + case ReflectionKind::Type: + OS << "type"; + break; + case ReflectionKind::Null: + OS << "null"; + break; + } + + return OS; +} + +/// [expr.reflect] p5, if a reflect-expression R matches the form +/// ^^reflection-name it is interpreted as such; the identifier is looked up +/// and the representation of R is determined as follows: +/// - if lookup finds a type alias A, R represents the type the underlying +/// entity of A if A was introduced by the declaration of a template +/// parameter; otherwise, R represents A. + +/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id, +/// if type-id is neither a placeholder type nor +/// in the form of nested-name-specifier_opt template_opt simple-template-id +/// then R represents the type denoted by the type-id + +// In particular, this means that e.g. '^^const Alias' is reflection of +// a type, not an alias. For example: +// +// using foo = const int; +// ^^int // Type +// ^^const int // Type +// ^^foo // Alias +// ^^const foo // Type +inline bool isTypeAliasAsReflectionName(QualType QT) { + return QT.getLocalQualifiers() == Qualifiers{}; +} + +} // namespace clang + +#endif diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index c9658775f0470..4886db6998b45 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -2766,6 +2766,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isUndeducedAutoType() const; // C++11 auto or // C++14 decltype(auto) bool isTypedefNameType() const; // typedef or alias template + bool isMetaInfoType() const; // C++26 std::meta::info #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ bool is##Id##Type() const; @@ -9051,6 +9052,10 @@ inline bool Type::isVoidType() const { return isSpecificBuiltinType(BuiltinType::Void); } +inline bool Type::isMetaInfoType() const { + return isSpecificBuiltinType(BuiltinType::MetaInfo); +} + inline bool Type::isHalfType() const { // FIXME: Should we allow complex __fp16? Probably not. return isSpecificBuiltinType(BuiltinType::Half); diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 3aba4d261a651..892ee746a00fb 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -96,6 +96,7 @@ struct TransferrableTargetInfo { unsigned char FloatWidth, FloatAlign; unsigned char DoubleWidth, DoubleAlign; unsigned char LongDoubleWidth, LongDoubleAlign, Float128Align, Ibm128Align; + unsigned char MetaInfoWidth, MetaInfoAlign; unsigned char LargeArrayMinWidth, LargeArrayAlign; unsigned char LongWidth, LongAlign; unsigned char LongLongWidth, LongLongAlign; @@ -828,6 +829,11 @@ class TargetInfo : public TransferrableTargetInfo, unsigned getIbm128Align() const { return Ibm128Align; } const llvm::fltSemantics &getIbm128Format() const { return *Ibm128Format; } + /// Returns the size of std::meta::info. + unsigned getMetaInfoWidth() const { return MetaInfoWidth; } + /// Returns the align of std::meta::info. + unsigned getMetaInfoAlign() const { return MetaInfoAlign; } + /// Return the mangled code of long double. virtual const char *getLongDoubleMangling() const { return "e"; } diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 671341488278e..e36e6ea29810f 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1155,6 +1155,9 @@ enum PredefinedTypeIDs { #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID, #include "clang/Basic/HLSLIntangibleTypes.def" + /// C++26 std::meta::info type. + PREDEF_TYPE_META_INFO_ID, + /// The placeholder type for unresolved templates. PREDEF_TYPE_UNRESOLVED_TEMPLATE, // Sentinel value. Considered a predefined type but not useable as one. @@ -1166,7 +1169,7 @@ enum PredefinedTypeIDs { /// /// Type IDs for non-predefined types will start at /// NUM_PREDEF_TYPE_IDs. -const unsigned NUM_PREDEF_TYPE_IDS = 529; +const unsigned NUM_PREDEF_TYPE_IDS = 530; // Ensure we do not overrun the predefined types we reserved // in the enum PredefinedTypeIDs above. diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index 727e5f8c00a10..7fa843b89b4d4 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -374,6 +374,10 @@ APValue::APValue(const APValue &RHS) MakeAddrLabelDiff(); setAddrLabelDiff(RHS.getAddrLabelDiffLHS(), RHS.getAddrLabelDiffRHS()); break; + case Reflection: + MakeReflection(RHS.getReflectionOperandKind(), + RHS.getReflectionOpaqueOperand()); + break; } } @@ -429,6 +433,8 @@ void APValue::DestroyDataAndMakeUninit() { ((MemberPointerData *)(char *)&Data)->~MemberPointerData(); else if (Kind == AddrLabelDiff) ((AddrLabelDiffData *)(char *)&Data)->~AddrLabelDiffData(); + else if (Kind == Reflection) + ((ReflectionData *)(char *)&Data)->~ReflectionData(); Kind = None; AllowConstexprUnknown = false; } @@ -438,6 +444,7 @@ bool APValue::needsCleanup() const { case None: case Indeterminate: case AddrLabelDiff: + case Reflection: return false; case Struct: case Union: @@ -486,6 +493,37 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) { ID.AddInteger((uint32_t)V.extractBitsAsZExtValue(std::min(32u, N - I), I)); } +/// Unwrap reflected type for profiling +static void profileTypeReflection(llvm::FoldingSetNodeID &ID, QualType QT) { + // TODO(Reflection) + + if (isTypeAliasAsReflectionName(QT)) { + if (const auto *TDT = QT->getAs<TypedefType>()) { + ID.AddBoolean(true); + ID.AddPointer(TDT->getDecl()->getCanonicalDecl()); + return; + } + } + + ID.AddBoolean(false); + QT.getCanonicalType().Profile(ID); +} + +static void profileReflection(llvm::FoldingSetNodeID &ID, APValue V) { + ID.AddInteger(static_cast<int>(V.getReflectionOperandKind())); + switch (V.getReflectionOperandKind()) { + case ReflectionKind::Null: + return; + case ReflectionKind::Type: { + const TypeSourceInfo *Info = + static_cast<const TypeSourceInfo *>(V.getReflectionOpaqueOperand()); + profileTypeReflection(ID, Info->getType()); + return; + } + } + assert(false && "unknown or unimplemented reflection entities"); +} + void APValue::Profile(llvm::FoldingSetNodeID &ID) const { // Note that our profiling assumes that only APValues of the same type are // ever compared. As a result, we don't consider collisions that could only @@ -632,6 +670,9 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const { for (const CXXRecordDecl *D : getMemberPointerPath()) ID.AddPointer(D); return; + case Reflection: + profileReflection(ID, *this); + return; } llvm_unreachable("Unknown APValue kind!"); @@ -986,6 +1027,19 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, Out << " - "; Out << "&&" << getAddrLabelDiffRHS()->getLabel()->getName(); return; + case APValue::Reflection: + switch (getReflectionOperandKind()) { + case ReflectionKind::Null: + Out << "std::meta::info{}"; + break; + case ReflectionKind::Type: { + const auto *TInfo = + static_cast<const TypeSourceInfo *>(getReflectionOpaqueOperand()); + Out << "^^" << TInfo->getType().stream(Policy); + break; + } + } + return; } llvm_unreachable("Unknown APValue kind!"); } @@ -1176,6 +1230,7 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V, case APValue::ComplexInt: case APValue::ComplexFloat: case APValue::Vector: + case APValue::Reflection: case APValue::Matrix: break; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2228811546c0f..c94598662915d 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1511,6 +1511,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target, // nullptr type (C++0x 2.14.7) InitBuiltinType(NullPtrTy, BuiltinType::NullPtr); + // std::meta::info type (C++26 21.4.1) + InitBuiltinType(MetaInfoTy, BuiltinType::MetaInfo); + // half type (OpenCL 6.1.1.1) / ARM NEON __fp16 InitBuiltinType(HalfTy, BuiltinType::Half); @@ -2366,6 +2369,10 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getPointerWidth(LangAS::Default); Align = Target->getPointerAlign(LangAS::Default); break; + case BuiltinType::MetaInfo: + Width = Target->getMetaInfoWidth(); + Align = Target->getMetaInfoAlign(); + break; case BuiltinType::ObjCId: case BuiltinType::ObjCClass: case BuiltinType::ObjCSel: @@ -3568,6 +3575,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx, case BuiltinType::VectorPair: case BuiltinType::DMR1024: case BuiltinType::DMR2048: + case BuiltinType::MetaInfo: OS << "?"; return; @@ -9325,6 +9333,7 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C, case BuiltinType::OCLReserveID: case BuiltinType::OCLSampler: case BuiltinType::Dependent: + case BuiltinType::MetaInfo: #define PPC_VECTOR_TYPE(Name, Id, Size) \ case BuiltinType::Id: #include "clang/Basic/PPCTypes.def" diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index db7d223d56af3..7e726eb155d38 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -686,6 +686,7 @@ namespace clang { ExpectedStmt VisitCXXThisExpr(CXXThisExpr *E); ExpectedStmt VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E); ExpectedStmt VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E); + ExpectedStmt VisitCXXReflectExpr(CXXReflectExpr *E); ExpectedStmt VisitMemberExpr(MemberExpr *E); ExpectedStmt VisitCallExpr(CallExpr *E); ExpectedStmt VisitLambdaExpr(LambdaExpr *LE); @@ -8861,6 +8862,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) { *ToTypeOrErr, *ToLocationOrErr); } +ExpectedStmt ASTNodeImporter::VisitCXXReflectExpr(CXXReflectExpr *E) { + Error Err = Error::success(); + auto ToOperatorLoc = importChecked(Err, E->getOperatorLoc()); + auto ToTSI = importChecked(Err, E->getTypeSourceInfo()); + if (Err) + return std::move(Err); + + return CXXReflectExpr::Create(Importer.getToContext(), ToOperatorLoc, ToTSI); +} + ExpectedStmt ASTNodeImporter::VisitMemberExpr(MemberExpr *E) { Error Err = Error::success(); auto ToBase = importChecked(Err, E->getBase()); @@ -10873,7 +10884,7 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) { } break; } - case APValue::LValue: + case APValue::LValue: { APValue::LValueBase Base; QualType FromElemTy; if (FromValue.getLValueBase()) { @@ -10944,6 +10955,27 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) { } else Result.setLValue(Base, Offset, APValue::NoLValuePath{}, FromValue.isNullPointer()); + break; + } + case APValue::Reflection: { + switch (FromValue.getReflectionOperandKind()) { + case ReflectionKind::Null: + Result = APValue(ReflectionKind::Null, nullptr); + break; + case ReflectionKind::Type: { + const auto *FromTSI = static_cast<const TypeSourceInfo *>( + FromValue.getReflectionOpaqueOperand()); + QualType ImpType = importChecked(Err, FromTSI->getType()); + if (Err) + return std::move(Err); + TypeSourceInfo *ToTSI = + Importer.ToContext.getTrivialTypeSourceInfo(ImpType); + Result = APValue(ReflectionKind::Type, ToTSI); + break; + } + } + break; + } } if (Err) return std::move(Err); diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 35667a9132680..ca285e89b2250 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -4521,6 +4521,22 @@ bool Compiler<Emitter>::VisitObjCArrayLiteral(const ObjCArrayLiteral *E) { return this->emitError(E); } +template <class Emitter> +bool Compiler<Emitter>::VisitCXXReflectExpr(const CXXReflectExpr *E) { + if (DiscardResult) + return true; + + switch (E->getKind()) { + case ReflectionKind::Type: { + return this->emitReflectValue(E->getKind(), E->getOpaqueValue(), E); + } + case ReflectionKind::Null: + llvm_unreachable("A null reflection should not reach here"); + } + + return false; +} + template <class Emitter> bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) { assert(Ctx.getLangOpts().CPlusPlus); @@ -5038,6 +5054,8 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT, auto Sem = Ctx.getASTContext().getFixedPointSemantics(QT); return this->emitConstFixedPoint(FixedPoint::zero(Sem), E); } + case PT_Reflect: + return this->emitReflectValue(ReflectionKind::Null, nullptr, E); } llvm_unreachable("unknown primitive type"); } @@ -5267,6 +5285,7 @@ bool Compiler<Emitter>::emitConst(T Value, PrimType Ty, SourceInfo Info) { case PT_IntAP: case PT_IntAPS: case PT_FixedPoint: + case PT_Reflect: llvm_unreachable("Invalid integral type"); break; } diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 9eb8069496099..3051ad9399266 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -232,6 +232,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, bool VisitCXXTypeidExpr(const CXXTypeidExpr *E); bool VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E); bool VisitObjCArrayLiteral(const ObjCArrayLiteral *E); + bool VisitCXXReflectExpr(const CXXReflectExpr *E); bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E); // Statements. diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index b913d2a9f539c..673c6adf5f96b 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -539,6 +539,9 @@ OptPrimType Context::classify(QualType T) const { if (T->isFixedPointType()) return PT_FixedPoint; + if (T->isMetaInfoType()) + return PT_Reflect; + // Vector and complex types get here. return std::nullopt; } diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index fb41c98dd68cb..1118267b61295 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -16,6 +16,7 @@ #include "Pointer.h" #include "PrimType.h" #include "Record.h" +#include "Reflect.h" #include "Source.h" #include "clang/AST/ExprCXX.h" diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp index 4caf830a0a1b4..4a2e47d427f91 100644 --- a/clang/lib/AST/ByteCode/Disasm.cpp +++ b/clang/lib/AST/ByteCode/Disasm.cpp @@ -318,6 +318,8 @@ static const char *primTypeToString(PrimType T) { return "MemberPtr"; case PT_FixedPoint: return "FixedPoint"; + case PT_Reflect: + return "Reflect"; } llvm_unreachable("Unhandled PrimType"); } diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index f59485ec306e4..ebe4a7256c7fd 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -3230,7 +3230,7 @@ constexpr bool OpReturns(Opcode Op) { Op == OP_RetSint64 || Op == OP_RetUint64 || Op == OP_RetIntAP || Op == OP_RetIntAPS || Op == OP_RetBool || Op == OP_RetFixedPoint || Op == OP_RetPtr || Op == OP_RetMemberPtr || Op == OP_RetFloat || - Op == OP_EndSpeculation; + Op == OP_RetReflect || Op == OP_EndSpeculation; } #if USE_TAILCALLS diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 405f4a29ec982..63f6ffc74c237 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -29,6 +29,7 @@ #include "MemberPointer.h" #include "PrimType.h" #include "Program.h" +#include "Reflect.h" #include "State.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" @@ -4090,6 +4091,12 @@ inline bool CheckDestruction(InterpState &S, CodePtr OpPC) { return checkDestructor(S, OpPC, Ptr); } +inline bool ReflectValue(InterpState &S, CodePtr OpPC, ReflectionKind Kind, + const void *Operand) { + S.Stk.push<Reflect>(Kind, Operand); + return true; +} + inline bool IsBaseClass(InterpState &S) { S.Stk.push<bool>(S.Stk.peek<Pointer>().isBaseClass()); return true; diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index f136301c02912..35edb5988b817 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -515,7 +515,7 @@ using PrimTypeVariant = Char<false>, Char<true>, Integral<16, false>, Integral<16, true>, Integral<32, false>, Integral<32, true>, Integral<64, false>, Integral<64, true>, IntegralAP<true>, - IntegralAP<false>, Boolean, Floating>; + IntegralAP<false>, Boolean, Floating, Reflect>; // NB: This implementation isn't exactly ideal, but: // 1) We can't just do a bitcast here since we need to be able to diff --git a/clang/lib/AST/ByteCode/InterpStack.h b/clang/lib/AST/ByteCode/InterpStack.h index 2c02979ee6eec..a83d173e0eb16 100644 --- a/clang/lib/AST/ByteCode/InterpStack.h +++ b/clang/lib/AST/ByteCode/InterpStack.h @@ -17,6 +17,7 @@ #include "IntegralAP.h" #include "MemberPointer.h" #include "PrimType.h" +#include "Reflect.h" namespace clang { namespace interp { @@ -203,6 +204,8 @@ class InterpStack final { return PT_MemberPtr; else if constexpr (std::is_same_v<T, FixedPoint>) return PT_FixedPoint; + else if constexpr (std::is_same_v<T, Reflect>) + return PT_Reflect; llvm_unreachable("unknown type push()'ed into InterpStack"); } diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index f6338cda56e34..ff1b6bada6223 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -10,6 +10,7 @@ #include "InterpFrame.h" #include "InterpStack.h" #include "Program.h" +#include "Reflect.h" #include "State.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index c2f0114c81957..891fb423a9d77 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -31,6 +31,7 @@ def Float : Type; def Ptr : Type; def MemberPtr : Type; def FixedPoint : Type; +def Reflect : Type; //===----------------------------------------------------------------------===// // Types transferred to the interpreter. @@ -71,6 +72,8 @@ def ArgDesc : ArgType { let Name = "const Descriptor *"; } def ArgPrimType : ArgType { let Name = "PrimType"; } def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; } def ArgTypePtr : ArgType { let Name = "const Type *"; } +def ArgVoidPtr : ArgType { let Name = "const void *"; } +def ArgReflectionKind : ArgType { let Name = "ReflectionKind"; } //===----------------------------------------------------------------------===// // Classes of types instructions operate on. @@ -121,7 +124,7 @@ def NonPtrTypeClass : TypeClass { } def AllTypeClass : TypeClass { - let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types, FloatTypeClass.Types); + let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types, FloatTypeClass.Types, [Reflect]); } def ComparableTypeClass : TypeClass { @@ -368,6 +371,10 @@ def CastMemberPtrDerivedPop : Opcode { let NeedsOpPC = 0; } +def ReflectValue : Opcode { + let Args = [ArgReflectionKind, ArgVoidPtr]; +} + def FinishInitPop : SuccessOpcode; def FinishInit : SuccessOpcode; def FinishInitActivate : SuccessOpcode; diff --git a/clang/lib/AST/ByteCode/PrimType.cpp b/clang/lib/AST/ByteCode/PrimType.cpp index 923233e5fb13a..0ba95bab5c6b5 100644 --- a/clang/lib/AST/ByteCode/PrimType.cpp +++ b/clang/lib/AST/ByteCode/PrimType.cpp @@ -14,6 +14,7 @@ #include "IntegralAP.h" #include "MemberPointer.h" #include "Pointer.h" +#include "Reflect.h" using namespace clang; using namespace clang::interp; diff --git a/clang/lib/AST/ByteCode/PrimType.h b/clang/lib/AST/ByteCode/PrimType.h index 8f725942fedb9..ce10ebf4835ec 100644 --- a/clang/lib/AST/ByteCode/PrimType.h +++ b/clang/lib/AST/ByteCode/PrimType.h @@ -26,6 +26,7 @@ class Boolean; class Floating; class MemberPointer; class FixedPoint; +class Reflect; template <bool Signed> class IntegralAP; template <bool Signed> class Char; template <unsigned Bits, bool Signed> class Integral; @@ -47,6 +48,7 @@ enum PrimType : uint8_t { PT_Float = 12, PT_Ptr = 13, PT_MemberPtr = 14, + PT_Reflect = 15, }; constexpr bool isIntegerOrBoolType(PrimType T) { return T <= PT_Bool; } @@ -193,6 +195,9 @@ template <> struct PrimConv<PT_MemberPtr> { template <> struct PrimConv<PT_FixedPoint> { using T = FixedPoint; }; +template <> struct PrimConv<PT_Reflect> { + using T = Reflect; +}; /// Returns the size of a primitive type in bytes. size_t primSize(PrimType Type); @@ -238,6 +243,7 @@ static inline bool aligned(const void *P) { TYPE_SWITCH_CASE(PT_Ptr, B) \ TYPE_SWITCH_CASE(PT_MemberPtr, B) \ TYPE_SWITCH_CASE(PT_FixedPoint, B) \ + TYPE_SWITCH_CASE(PT_Reflect, B) \ } \ } while (0) diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index 378a190184be9..5f0ca50a41f4c 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -12,6 +12,7 @@ #include "Function.h" #include "Integral.h" #include "PrimType.h" +#include "Reflect.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h index b0ef993258637..764e5ee4b5c70 100644 --- a/clang/lib/AST/ByteCode/Program.h +++ b/clang/lib/AST/ByteCode/Program.h @@ -17,6 +17,7 @@ #include "Pointer.h" #include "PrimType.h" #include "Record.h" +#include "Reflect.h" #include "Source.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Allocator.h" diff --git a/clang/lib/AST/ByteCode/Reflect.h b/clang/lib/AST/ByteCode/Reflect.h new file mode 100644 index 0000000000000..bfac6f72be8ba --- /dev/null +++ b/clang/lib/AST/ByteCode/Reflect.h @@ -0,0 +1,60 @@ +//===--- Reflect.h - Reflection type for the constexpr VM -------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_REFLECT_H +#define LLVM_CLANG_AST_INTERP_REFLECT_H + +#include "clang/AST/APValue.h" +#include "clang/AST/ComparisonCategories.h" +#include "clang/AST/Reflection.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include <cstddef> +#include <cstdint> + +namespace clang { +namespace interp { + +class Reflect final { +private: + ReflectionKind Kind; + const void *Operand; + +public: + Reflect() : Kind(ReflectionKind::Null), Operand(nullptr) {} + Reflect(ReflectionKind Kind, const void *Operand) + : Kind(Kind), Operand(Operand) {} + + ComparisonCategoryResult compare(const Reflect &RHS) const { + llvm::FoldingSetNodeID LID, RID; + APValue(Kind, Operand).Profile(LID); + APValue(RHS.Kind, RHS.Operand).Profile(RID); + + if (LID == RID) + return ComparisonCategoryResult::Equal; + return ComparisonCategoryResult::Unordered; + } + + void print(llvm::raw_ostream &OS) const { + OS << "Reflect(" << Kind << ", " << Operand << ")"; + } + APValue toAPValue(const ASTContext &Ctx) const { + return APValue(Kind, Operand); + } +}; + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Reflect &R) { + R.print(OS); + return OS; +} + +} // namespace interp +} // namespace clang + +#endif diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 6c1cde6540d85..f77999d8bada9 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -24,6 +24,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/LambdaCapture.h" #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/Reflection.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -1934,15 +1935,15 @@ TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C, 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(ASTContext &C, SourceLocation CaretCaretLoc, + TypeSourceInfo *TSI) + : Expr(CXXReflectExprClass, C.MetaInfoTy, VK_PRValue, OK_Ordinary), + CaretCaretLoc(CaretCaretLoc), Kind(ReflectionKind::Type), Operand(TSI) {} CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, SourceLocation CaretCaretLoc, TypeSourceInfo *TSI) { - return new (C) CXXReflectExpr(CaretCaretLoc, TSI); + return new (C) CXXReflectExpr(C, CaretCaretLoc, TSI); } CXXReflectExpr *CXXReflectExpr::CreateEmpty(ASTContext &C) { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 574dd8b04e779..c1393b619e7ff 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -48,6 +48,7 @@ #include "clang/AST/OSLog.h" #include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/Reflection.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -2644,6 +2645,7 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) { case APValue::Struct: case APValue::Union: case APValue::AddrLabelDiff: + case APValue::Reflection: return false; } @@ -7984,7 +7986,8 @@ class APValueToBufferConverter { case APValue::Matrix: case APValue::Union: case APValue::MemberPointer: - case APValue::AddrLabelDiff: { + case APValue::AddrLabelDiff: + case APValue::Reflection: { Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unsupported_type) << Ty; @@ -11220,6 +11223,57 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { return true; } + +//===----------------------------------------------------------------------===// +// Reflection expression evaluation +//===----------------------------------------------------------------------===// + +namespace { +class ReflectionEvaluator : public ExprEvaluatorBase<ReflectionEvaluator> { + + using BaseType = ExprEvaluatorBase<ReflectionEvaluator>; + + APValue &Result; + +public: + ReflectionEvaluator(EvalInfo &E, APValue &Result) + : ExprEvaluatorBaseTy(E), Result(Result) {} + + bool Success(const APValue &V, const Expr *E) { + Result = V; + return true; + } + + bool VisitCXXReflectExpr(const CXXReflectExpr *E); + bool ZeroInitialization(const Expr *E); +}; + +bool ReflectionEvaluator::VisitCXXReflectExpr(const CXXReflectExpr *E) { + switch (E->getKind()) { + case ReflectionKind::Null: { + APValue ReflectionValue(ReflectionKind::Null, /*Operand=*/nullptr); + return Success(ReflectionValue, E); + } + case ReflectionKind::Type: { + APValue ReflectionValue(ReflectionKind::Type, E->getOpaqueValue()); + return Success(ReflectionValue, E); + } + } + llvm_unreachable("invalid reflection"); +} + +bool ReflectionEvaluator::ZeroInitialization(const Expr *E) { + Result = APValue(ReflectionKind::Null, /*Operand=*/nullptr); + return true; +} + +} // end anonymous namespace + +static bool EvaluateReflection(const Expr *E, APValue &Result, EvalInfo &Info) { + assert(E->isPRValue() && E->getType()->isMetaInfoType()); + return ReflectionEvaluator(Info, Result).Visit(E); +} + //===----------------------------------------------------------------------===// // Member Pointer Evaluation //===----------------------------------------------------------------------===// @@ -16319,6 +16373,7 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T, #include "clang/Basic/AMDGPUTypes.def" #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id: #include "clang/Basic/HLSLIntangibleTypes.def" + case BuiltinType::MetaInfo: return GCCTypeClass::None; case BuiltinType::Dependent: @@ -19431,6 +19486,23 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, return Success(CmpResult::Equal, E); } + if (LHSTy->isMetaInfoType() && RHSTy->isMetaInfoType()) { + APValue LHSValue, RHSValue; + llvm::FoldingSetNodeID LID, RID; + if (!Evaluate(LHSValue, Info, E->getLHS())) + return false; + LHSValue.Profile(LID); + + if (!Evaluate(RHSValue, Info, E->getRHS())) + return false; + RHSValue.Profile(RID); + + if (LID == RID) + return Success(CmpResult::Equal, E); + else + return Success(CmpResult::Unequal, E); + } + return DoAfter(); } @@ -21607,6 +21679,9 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { } else if (T->isIntegralOrEnumerationType()) { if (!IntExprEvaluator(Info, Result).Visit(E)) return false; + } else if (T->isMetaInfoType()) { + if (!EvaluateReflection(E, Result, Info)) + return false; } else if (T->hasPointerRepresentation()) { LValue LV; if (!EvaluatePointer(E, LV, Info)) diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 172a19f8f3537..7a20185400419 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -575,6 +575,7 @@ class CXXNameMangler { void mangleFloatLiteral(QualType T, const llvm::APFloat &V); void mangleFixedPointLiteral(); void mangleNullPointer(QualType T); + void mangleReflection(ReflectionKind Kind, const void *OpaqueOperand); void mangleMemberExprBase(const Expr *base, bool isArrow); void mangleMemberExpr(const Expr *base, bool isArrow, @@ -1273,6 +1274,66 @@ void CXXNameMangler::mangleNullPointer(QualType T) { Out << "0E"; } +void CXXNameMangler::mangleReflection(ReflectionKind Kind, + const void *OpaqueOperand) { + // https://github.com/itanium-cxx-abi/cxx-abi/issues/208 + // TODO(Reflection): add support for remaining items in the grammar below + + // <reflection> ::= nu # null reflection + // ::= vl <expression> # value + // ::= ob <expression> # object + // ::= vr <variable name> # variable + // ::= sb <sb name> # structured binding + // ::= fn <function encoding> # function + // ::= pa [ <nonnegative number> ] _ <encoding> # function + // parameter + // ::= en <prefix> <unqualified-name> # enumerator + // ::= an [ <nonnegative number> ] _ # annotation + // ::= ta <alias prefix> # type alias + // ::= ty <type> # type + // ::= dm <prefix> <unqualified-name> # non-static data + // member + // ::= un <prefix> [ <nonnegative number> ] _ # unnamed bit-field + // ::= ct [ <prefix> ] <unqualified-name> # class template + // ::= ft [ <prefix> ] <unqualified-name> # function template + // ::= vt [ <prefix> ] <unqualified-name> # variable template + // ::= at [ <prefix> ] <unqualified-name> # alias template + // ::= co [ <prefix> ] <unqualified-name> # concept + // ::= na [ <prefix> ] <unqualified-name> # namespace alias + // ::= ns [ <prefix> ] <unqualified-name> # namespace + // ::= ng # ^^:: + // ::= ba [ <nonnegative number> ] _ <type> # direct base class + // relationship + // ::= ds <type> _ [ <unqualified-name> ] _ + // [ <alignment number> ] _ [ <bit-width number> ] _ + // [ n ] # data member + // description + + Out << "LDm"; + switch (Kind) { + case ReflectionKind::Null: + Out << "nu"; + break; + case ReflectionKind::Type: { + const auto *TSI = static_cast<const TypeSourceInfo *>(OpaqueOperand); + QualType QT = TSI->getType(); + + if (isTypeAliasAsReflectionName(QT)) { + if (const auto *TDT = QT->getAs<TypedefType>()) { + Out << "ta"; + mangleName(TDT->getDecl()->getCanonicalDecl()); + break; + } + } + + Out << "ty"; + mangleType(QT); + break; + } + } + Out << 'E'; +} + void CXXNameMangler::mangleNumber(const llvm::APSInt &Value) { if (Value.isSigned() && Value.isNegative()) { Out << 'n'; @@ -3148,10 +3209,12 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { // UNSUPPORTED: ::= De # IEEE 754r decimal floating point (128 bits) // UNSUPPORTED: ::= Df # IEEE 754r decimal floating point (32 bits) // ::= Dh # IEEE 754r half-precision floating point (16 bits) - // ::= DF <number> _ # ISO/IEC TS 18661 binary floating point type _FloatN (N bits); + // ::= DF <number> _ # ISO/IEC TS 18661 binary floating point + // type _FloatN (N bits); // ::= Di # char32_t // ::= Ds # char16_t // ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) + // ::= Dm # std::meta::info (i.e., decltype(^^int)) // ::= [DS] DA # N1169 fixed-point [_Sat] T _Accum // ::= [DS] DR # N1169 fixed-point [_Sat] T _Fract // ::= u <source-name> # vendor extended type @@ -3420,6 +3483,10 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { Out << TI->getIbm128Mangling(); break; } + case BuiltinType::MetaInfo: + // https://github.com/itanium-cxx-abi/cxx-abi/issues/208 + Out << "Dm"; + break; case BuiltinType::NullPtr: Out << "Dn"; break; @@ -4936,6 +5003,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, // ::= L <pointer type> 0 E # null pointer template argument // ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C99); not used by clang // ::= L <mangled-name> E # external name + // ::= LDm <reflection> E # C++26 reflection value // clang-format on QualType ImplicitlyConvertedToType; @@ -5015,8 +5083,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, goto recurse; case Expr::CXXReflectExprClass: { - // TODO(Reflection): implement this after introducing std::meta::info - assert(false && "unimplemented"); + const CXXReflectExpr *RE = cast<CXXReflectExpr>(E); + mangleReflection(RE->getKind(), RE->getOpaqueValue()); break; } @@ -6554,6 +6622,9 @@ static bool isZeroInitialized(QualType T, const APValue &V) { case APValue::MemberPointer: return !V.getMemberPointerDecl(); + + case APValue::Reflection: + return !V.getReflectionOpaqueOperand(); } llvm_unreachable("Unhandled APValue::ValueKind enum"); @@ -6950,6 +7021,12 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V, break; } + case APValue::Reflection: { + mangleReflection(V.getReflectionOperandKind(), + V.getReflectionOpaqueOperand()); + break; + } + case APValue::MemberPointer: // Proposed in https://github.com/itanium-cxx-abi/cxx-abi/issues/47. if (!V.getMemberPointerDecl()) { diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 59732ff0fed11..06abe01553b43 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -2168,6 +2168,11 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, Error("template argument (value type: fixed point)"); return; } + + case APValue::Reflection: { + Error("template argument (value type: reflection)"); + return; + } } } diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp index 17f5ee5dee3d1..70d56c96f5bdf 100644 --- a/clang/lib/AST/NSAPI.cpp +++ b/clang/lib/AST/NSAPI.cpp @@ -471,6 +471,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const { case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: case BuiltinType::BFloat16: + case BuiltinType::MetaInfo: break; } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 9a97d52fd3020..6647b5975e7d0 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2635,8 +2635,15 @@ void StmtPrinter::VisitCXXUnresolvedConstructExpr( } void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) { - // TODO(Reflection): Implement this. - assert(false && "not implemented yet"); + // TODO(Reflection): add support for the remaining reflection kinds. + OS << "^^"; + switch (S->getKind()) { + case ReflectionKind::Null: + break; + case ReflectionKind::Type: + S->getTypeSourceInfo()->getType().print(OS, Policy); + break; + } } void StmtPrinter::VisitCXXDependentScopeMemberExpr( diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index fdefb6d6b2e1f..8af5efa66e9d1 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2237,8 +2237,28 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) { } void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) { - // TODO(Reflection): Implement this. - assert(false && "not implemented yet"); + // TODO(Reflection): Add support for Null, TypeSourceInfo, + // TemplateReference and DeclRefExpr + VisitExpr(E); + ID.AddInteger(static_cast<int>(E->getKind())); + switch (E->getKind()) { + case ReflectionKind::Null: + return; + case ReflectionKind::Type: { + QualType QT = E->getTypeSourceInfo()->getType(); + if (isTypeAliasAsReflectionName(QT)) { + if (const auto *TDT = QT->getAs<TypedefType>()) { + ID.AddBoolean(true); + VisitDecl(TDT->getDecl()->getCanonicalDecl()); + return; + } + } + ID.AddBoolean(false); + VisitType(QT); + return; + } + } + assert(false && "unknown or unimplemented reflection entities"); } void diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 5d724e9d1d91e..67b268af2f4c8 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -616,6 +616,7 @@ static bool isSimpleAPValue(const APValue &Value) { case APValue::LValue: case APValue::MemberPointer: case APValue::AddrLabelDiff: + case APValue::Reflection: return true; case APValue::Vector: case APValue::Array: @@ -875,6 +876,21 @@ void TextNodeDumper::Visit(const APValue &Value, QualType Ty) { OS << " - "; OS << "&&" << Value.getAddrLabelDiffRHS()->getLabel()->getName(); return; + case APValue::Reflection: + OS << "Reflection "; + switch (Value.getReflectionOperandKind()) { + case ReflectionKind::Null: + OS << "std::meta::info{}"; + break; + case ReflectionKind::Type: { + OS << "^^"; + const TypeSourceInfo *TSI = static_cast<const TypeSourceInfo *>( + Value.getReflectionOpaqueOperand()); + TSI->getType().print(OS, PrintPolicy); + break; + } + } + return; } llvm_unreachable("Unknown APValue kind!"); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 42d148715bc40..2f94b4664785e 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3171,6 +3171,13 @@ bool Type::isLiteralType(const ASTContext &Ctx) const { return true; } + // C++26 [basic.types]p9: + // -- std::meta::info is a scalar type + // C++26 [basic.types]p10: + // -- a scalar type is a literal type + if (isMetaInfoType()) + return true; + // We treat _Atomic T as a literal type if T is a literal type. if (const auto *AT = BaseTy->getAs<AtomicType>()) return AT->getValueType()->isLiteralType(Ctx); @@ -3546,6 +3553,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return "unsigned _Accum"; case ULongAccum: return "unsigned long _Accum"; + case BuiltinType::MetaInfo: + return "std::meta::info"; case BuiltinType::ShortFract: return "short _Fract"; case BuiltinType::Fract: @@ -5260,6 +5269,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { #include "clang/Basic/HLSLIntangibleTypes.def" case BuiltinType::BuiltinFn: case BuiltinType::NullPtr: + case BuiltinType::MetaInfo: case BuiltinType::IncompleteMatrixIdx: case BuiltinType::ArraySection: case BuiltinType::OMPArrayShaping: diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 7e72a85136966..e8c018eb92e23 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -426,6 +426,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const { case BuiltinType::ArraySection: case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: + case BuiltinType::MetaInfo: return TST_unspecified; } diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 46b5bdecb9c40..e429f4a76e564 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -126,6 +126,8 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) { LongDoubleAlign = 64; Float128Align = 128; Ibm128Align = 128; + MetaInfoWidth = 64; + MetaInfoAlign = 8; LargeArrayMinWidth = 0; LargeArrayAlign = 0; MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 7421733efcc24..42ad4673b792b 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1216,6 +1216,8 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { case BuiltinType::SatULongFract: Encoding = llvm::dwarf::DW_ATE_unsigned_fixed; break; + case BuiltinType::MetaInfo: + llvm_unreachable("std::meta::info is consteval-only type"); } BTName = BT->getName(CGM.getLangOpts()); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 257de0145855b..f865022259978 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -2752,6 +2752,8 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType, } case APValue::MemberPointer: return CGM.getCXXABI().EmitMemberPointer(Value, DestType); + case APValue::Reflection: + llvm_unreachable("std::meta::info is consteval-only type"); } llvm_unreachable("Unknown APValue kind"); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d1119e6c00d14..da4f27b82e475 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6366,6 +6366,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, if (getLangOpts().OpenCL && ASTTy->isSamplerT()) return; + // TODO(Reflection): add support for consteval-only types. + // HLSL default buffer constants will be emitted during HLSLBufferDecl codegen if (getLangOpts().HLSL && D->getType().getAddressSpace() == LangAS::hlsl_constant) diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index 55fe216580314..f559ac76f7984 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -487,6 +487,13 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { ResultType = llvm::PointerType::getUnqual(getLLVMContext()); break; + case BuiltinType::MetaInfo: + // FIXME(Reflection): once consteval-only types are supported, + // make this an llvm_unreachable instead because reflection + // should not reach here + ResultType = llvm::IntegerType::get(getLLVMContext(), 64); + break; + case BuiltinType::UInt128: case BuiltinType::Int128: ResultType = llvm::IntegerType::get(getLLVMContext(), 128); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index b17478cb7ffde..80696c846c816 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3813,6 +3813,7 @@ static bool TypeInfoIsInStandardLibrary(const BuiltinType *Ty) { case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::MetaInfo: return false; case BuiltinType::Dependent: diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp index 31d9250a48ec9..f41e1fa6c9763 100644 --- a/clang/lib/CodeGen/QualTypeMapper.cpp +++ b/clang/lib/CodeGen/QualTypeMapper.cpp @@ -156,6 +156,9 @@ QualTypeMapper::convertBuiltinType(const BuiltinType *BT) { return Builder.getIntegerType(1, getTypeAlign(QT), /*Signed=*/false, /*IsBitInt=*/false); + case BuiltinType::MetaInfo: + llvm::reportFatalInternalError("std::meta::info is consteval-only type"); + case BuiltinType::Char_S: case BuiltinType::Char_U: case BuiltinType::SChar: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index b844670543a55..3cc4cd0274ef3 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13040,6 +13040,13 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression); }; + if (LHSType->isMetaInfoType() && RHSType->isMetaInfoType()) { + if (!BinaryOperator::isEqualityOp(Opc)) { + return InvalidOperands(Loc, LHS, RHS); + } + return computeResultTy(); + } + if (!IsOrdered && LHSIsNull != RHSIsNull) { bool IsEquality = Opc == BO_EQ; if (RHSIsNull) @@ -16392,7 +16399,11 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, << resultType << Input.get()->getSourceRange()); } - if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { + if (resultType->isScalarType() && !isScopedEnumerationType(resultType) && + !resultType->isMetaInfoType()) { + // Before C++26, scalar types are contextually converted to bool, + // std::meta::info is a scalar type but not an arithmetic type. + // C99 6.5.3.3p1: ok, fallthrough; if (Context.getLangOpts().CPlusPlus) { // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index eafda32198f11..e43e91ced930e 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -9108,6 +9108,10 @@ class BuiltinCandidateTypeSet { /// candidate set. bool HasNullPtrType; + /// A flag indicating whether the reflection type was present in the + /// candidate set. + bool HasReflectionType; + /// Sema - The semantic analysis instance where we are building the /// candidate type set. Sema &SemaRef; @@ -9124,11 +9128,9 @@ class BuiltinCandidateTypeSet { typedef TypeSet::iterator iterator; BuiltinCandidateTypeSet(Sema &SemaRef) - : HasNonRecordTypes(false), - HasArithmeticOrEnumeralTypes(false), - HasNullPtrType(false), - SemaRef(SemaRef), - Context(SemaRef.Context) { } + : HasNonRecordTypes(false), HasArithmeticOrEnumeralTypes(false), + HasNullPtrType(false), HasReflectionType(false), SemaRef(SemaRef), + Context(SemaRef.Context) {} void AddTypesConvertedFrom(QualType Ty, SourceLocation Loc, @@ -9151,6 +9153,7 @@ class BuiltinCandidateTypeSet { bool hasNonRecordTypes() { return HasNonRecordTypes; } bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; } bool hasNullPtrType() const { return HasNullPtrType; } + bool hasReflectionType() const { return HasReflectionType; } }; } // end anonymous namespace @@ -9332,6 +9335,8 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, MatrixTypes.insert(Ty); } else if (Ty->isNullPtrType()) { HasNullPtrType = true; + } else if (Ty->isMetaInfoType()) { + HasReflectionType = true; } else if (AllowUserConversions && TyIsRec) { // No conversion functions in incomplete types. if (!SemaRef.isCompleteType(Loc, Ty)) @@ -9811,6 +9816,14 @@ class BuiltinOperatorOverloadBuilder { S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet); } } + + if (CandidateTypes[ArgIdx].hasReflectionType()) { + CanQualType MetaInfoTy = S.Context.MetaInfoTy; + if (AddedTypes.insert(MetaInfoTy).second) { + QualType ParamTypes[2] = {MetaInfoTy, MetaInfoTy}; + S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet); + } + } } } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 0d1e01df75d94..d9a8b2fea89fe 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8111,6 +8111,18 @@ static Expr *BuildExpressionFromIntegralTemplateArgumentValue( return E; } +/// Construct a new reflect expression that refers to the given +/// entity with the given source-location of the reflection operator. +static ExprResult BuildExpressionFromReflection(Sema &S, const APValue &RV, + SourceLocation CaretCaretLoc) { + // TODO(Reflection): Add support for NamespaceReference, TemplateReference, + // and DeclRefExpr. + return CXXReflectExpr::Create( + S.Context, CaretCaretLoc, + static_cast<TypeSourceInfo *>( + const_cast<void *>(RV.getReflectionOpaqueOperand()))); +} + static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( Sema &S, QualType T, const APValue &Val, SourceLocation Loc) { auto MakeInitList = [&](ArrayRef<Expr *> Elts) -> Expr * { @@ -8176,7 +8188,7 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( case APValue::Indeterminate: llvm_unreachable("Unexpected APValue kind."); case APValue::LValue: - case APValue::MemberPointer: + case APValue::MemberPointer: { // There isn't necessarily a valid equivalent source-level syntax for // these; in particular, a naive lowering might violate access control. // So for now we lower to a ConstantExpr holding the value, wrapped around @@ -8190,6 +8202,9 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( auto *OVE = new (S.Context) OpaqueValueExpr(Loc, T, VK); return ConstantExpr::Create(S.Context, OVE, Val); } + case APValue::Reflection: + return BuildExpressionFromReflection(S, Val, Loc).get(); + } llvm_unreachable("Unhandled APValue::ValueKind enum"); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index cc126f9000717..a32fccfc55ff4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13395,7 +13395,19 @@ StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt( template <typename Derived> ExprResult TreeTransform<Derived>::TransformCXXReflectExpr(CXXReflectExpr *E) { // TODO(reflection): Implement its transform - assert(false && "not implemented yet"); + + switch (E->getKind()) { + case ReflectionKind::Type: { + TypeSourceInfo *NewT = getDerived().TransformType(E->getTypeSourceInfo()); + if (!NewT) + return ExprError(); + return SemaRef.BuildCXXReflectExpr(E->getOperatorLoc(), NewT); + } + case ReflectionKind::Null: + llvm_unreachable("A null reflection should not reach here"); + } + + assert(false && "unknown or unimplemented reflection entities"); return ExprError(); } diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 74265d5fce908..0dae1d6a35c8b 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -286,6 +286,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::BFloat16: ID = PREDEF_TYPE_BFLOAT16_ID; break; + case BuiltinType::MetaInfo: + ID = PREDEF_TYPE_META_INFO_ID; + break; } return TypeIdx(0, ID); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index a018a378e9f44..9e690dadd25b9 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -8058,6 +8058,9 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_NULLPTR_ID: T = Context.NullPtrTy; break; + case PREDEF_TYPE_META_INFO_ID: + T = Context.MetaInfoTy; + break; case PREDEF_TYPE_CHAR8_ID: T = Context.Char8Ty; break; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 87cec16a76323..4ded925d08a38 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -540,8 +540,19 @@ void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) { } void ASTStmtReader::VisitCXXReflectExpr(CXXReflectExpr *E) { - // TODO(Reflection): Implement this. - assert(false && "not implemented yet"); + // TODO(Reflection): add support for TemplateReference, NamespaceReference and + // DeclRefExpr + VisitExpr(E); + E->CaretCaretLoc = readSourceLocation(); + E->Kind = static_cast<ReflectionKind>(Record.readInt()); + switch (E->Kind) { + case ReflectionKind::Null: + E->Operand = nullptr; + break; + case ReflectionKind::Type: + E->Operand = Record.readTypeSourceInfo(); + break; + } } void ASTStmtReader::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 70477f4cf4001..476ffe8726c66 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -475,8 +475,19 @@ void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *E) { } void ASTStmtWriter::VisitCXXReflectExpr(CXXReflectExpr *E) { - // TODO(Reflection): Implement this. - assert(false && "not implemented yet"); + // TODO(Reflection): add support for TemplateReference, NamespaceReference and + // DeclRefExpr + VisitExpr(E); + Record.AddSourceLocation(E->getOperatorLoc()); + Record.push_back(static_cast<uint64_t>(E->getKind())); + switch (E->getKind()) { + case ReflectionKind::Null: + break; + case ReflectionKind::Type: + Record.AddTypeSourceInfo(E->getTypeSourceInfo()); + break; + } + Code = serialization::EXPR_REFLECT; } void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { diff --git a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp index 5f689ad32159b..99901e9601ac0 100644 --- a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp +++ b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp @@ -805,6 +805,9 @@ void USRGenerator::VisitType(QualType T) { case BuiltinType::NullPtr: Out << 'n'; break; + case BuiltinType::MetaInfo: + Out << "@BT@MetaInfo"; + break; #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ case BuiltinType::Id: \ Out << "@BT@" << #Suffix << "_" << #ImgType; \ diff --git a/clang/test/AST/ast-dump-APValue-reflection.cpp b/clang/test/AST/ast-dump-APValue-reflection.cpp new file mode 100644 index 0000000000000..55fee355ebf1e --- /dev/null +++ b/clang/test/AST/ast-dump-APValue-reflection.cpp @@ -0,0 +1,21 @@ +// Test without serialization: +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++26 -freflection \ +// RUN: -ast-dump %s -ast-dump-filter Test \ +// RUN: | FileCheck --strict-whitespace %s +// +// Test with serialization: +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++26 -freflection -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -triple x86_64-unknown-unknown -std=c++26 -freflection \ +// RUN: -include-pch %t -ast-dump-all -ast-dump-filter Test /dev/null \ +// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \ +// RUN: | FileCheck --strict-whitespace %s + +void TestReflection() { + constexpr auto x = ^^int; + // CHECK: | `-VarDecl {{.*}} x 'const std::meta::info' constexpr cinit + // CHECK-NEXT: | |-value: Reflection ^^int + + constexpr decltype(^^int) y{}; + // CHECK: `-VarDecl {{.*}} y {{.*}} constexpr listinit + // CHECK-NEXT: |-value: Reflection std::meta::info{} +} diff --git a/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp b/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp new file mode 100644 index 0000000000000..a6266165e77f8 --- /dev/null +++ b/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp @@ -0,0 +1,7 @@ +// 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}} + return 0; +} diff --git a/clang/test/CodeGenCXX/reflection-mangle-ms.cpp b/clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp similarity index 100% rename from clang/test/CodeGenCXX/reflection-mangle-ms.cpp rename to clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp diff --git a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp index a6266165e77f8..e07731cc5b585 100644 --- a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp +++ b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp @@ -1,7 +1,48 @@ // RUN: %clang_cc1 -std=c++26 -freflection -triple x86_64-unknown-linux-gnu \ -// RUN: -emit-llvm -o - %s -verify +// RUN: -emit-llvm -o - %s | FileCheck %s + +using info = decltype(^^int); +using Type = int; + +template <auto A> +void foo () {} int main() { - (void)(^^int); // expected-error {{cannot compile this scalar expression yet}} + foo <info {}> (); + // CHECK: @_Z3fooITnDaLDmnuEEvv + foo <^^void> (); + // CHECK: @_Z3fooITnDaLDmtyvEEvv + foo <^^bool> (); + // CHECK: @_Z3fooITnDaLDmtybEEvv + foo <^^char> (); + // CHECK: @_Z3fooITnDaLDmtycEEvv + foo <^^signed char> (); + // CHECK: @_Z3fooITnDaLDmtyaEEvv + foo <^^unsigned char> (); + // CHECK: @_Z3fooITnDaLDmtyhEEvv + foo <^^short> (); + // CHECK: @_Z3fooITnDaLDmtysEEvv + foo <^^unsigned short> (); + // CHECK: @_Z3fooITnDaLDmtytEEvv + foo <^^int> (); + // CHECK: @_Z3fooITnDaLDmtyiEEvv + foo <^^unsigned int> (); + // CHECK: @_Z3fooITnDaLDmtyjEEvv + foo <^^long> (); + // CHECK: @_Z3fooITnDaLDmtylEEvv + foo <^^unsigned long> (); + // CHECK: @_Z3fooITnDaLDmtymEEvv + foo <^^long long> (); + // CHECK: @_Z3fooITnDaLDmtyxEEvv + foo <^^unsigned long long> (); + // CHECK: @_Z3fooITnDaLDmtyyEEvv + foo <^^float> (); + // CHECK: @_Z3fooITnDaLDmtyfEEvv + foo <^^double> (); + // CHECK: @_Z3fooITnDaLDmtydEEvv + foo <^^long double> (); + // CHECK: @_Z3fooITnDaLDmtyeEEvv + foo <^^Type> (); + // CHECK: @_Z3fooITnDaLDmta4TypeEEvv return 0; } diff --git a/clang/test/PCH/reflection.cpp b/clang/test/PCH/reflection.cpp new file mode 100644 index 0000000000000..73e98ea68c7ba --- /dev/null +++ b/clang/test/PCH/reflection.cpp @@ -0,0 +1,14 @@ +// Test without PCH +// RUN: %clang_cc1 -std=c++26 -freflection %s -include %S/reflection_include.h -fsyntax-only -verify + +// RUN: %clang_cc1 -std=c++26 -freflection -x c++-header %S/reflection_include.h -emit-pch -o %t +// RUN: %clang_cc1 -std=c++26 -freflection -include-pch %t -verify %s + +// expected-no-diagnostics + + +static_assert(^^int == ^^int); +static_assert(^^int != ^^double); +static_assert(info{} != ^^int); +static_assert(__is_same(decltype(^^int), info)); +static_assert(__is_same(decltype(^^double), info)); diff --git a/clang/test/PCH/reflection_include.h b/clang/test/PCH/reflection_include.h new file mode 100644 index 0000000000000..782aff5bbd3fe --- /dev/null +++ b/clang/test/PCH/reflection_include.h @@ -0,0 +1 @@ +using info = decltype(^^int); diff --git a/clang/test/Sema/reflection-meta-info.fail.cpp b/clang/test/Sema/reflection-meta-info.fail.cpp new file mode 100644 index 0000000000000..e4332990f5e29 --- /dev/null +++ b/clang/test/Sema/reflection-meta-info.fail.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++26 -freflection -fexperimental-new-constant-interpreter -fsyntax-only -verify + +using info = decltype(^^int); + +struct X { int a; }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::meta::info' to 'const X' for 1st argument}} \ + // expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::meta::info' to 'X' for 1st argument}} \ + // expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} +template <typename T = X, auto ptm = &X::a> +constexpr auto ptmOp = ((T)(^^int)).*ptm; // expected-error {{no matching conversion for C-style cast from 'std::meta::info' to 'X'}} +constexpr auto var = ptmOp<>; // expected-note {{in instantiation of variable template specialization 'ptmOp' requested here}} + +static_assert(^^int == ^^float); // expected-error {{static assertion failed due to requirement '^^int == ^^float'}} +static_assert(info{} == ^^int); // expected-error {{static assertion failed due to requirement 'std::meta::info{} == ^^int'}} + +consteval void test() +{ + (^^char)++; // expected-error {{cannot increment value of type 'std::meta::info'}} + (^^short)++; // expected-error {{cannot increment value of type 'std::meta::info'}} + (^^int)++; // expected-error {{cannot increment value of type 'std::meta::info'}} + (^^char)--; // expected-error {{cannot decrement value of type 'std::meta::info'}} + (^^short)--; // expected-error {{cannot decrement value of type 'std::meta::info'}} + (^^int)--; // expected-error {{cannot decrement value of type 'std::meta::info'}} + + ++(^^char); // expected-error {{cannot increment value of type 'std::meta::info'}} + ++(^^short); // expected-error {{cannot increment value of type 'std::meta::info'}} + ++(^^int); // expected-error {{cannot increment value of type 'std::meta::info'}} + --(^^char); // expected-error {{cannot decrement value of type 'std::meta::info'}} + --(^^short); // expected-error {{cannot decrement value of type 'std::meta::info'}} + --(^^int); // expected-error {{cannot decrement value of type 'std::meta::info'}} + + ~(^^int); // expected-error {{invalid argument type 'std::meta::info' to unary expression}} + !(^^float); // expected-error {{invalid argument type 'std::meta::info' to unary expression}} + + (^^int) + (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^double) - (^^float); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^char) * (^^short); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^long) / (^^long); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + + (^^int) & (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) | (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) ^ (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + + (^^int) < (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) > (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) <= (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) >= (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) <=>(^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + + // expected-error@+2 {{value of type 'std::meta::info' is not contextually convertible to 'bool'}} + // expected-error@+1 {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) && (^^int); + // expected-error@+2 {{value of type 'std::meta::info' is not contextually convertible to 'bool'}} + // expected-error@+1 {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}} + (^^int) || (^^int); +} diff --git a/clang/test/Sema/reflection-meta-info.pass.cpp b/clang/test/Sema/reflection-meta-info.pass.cpp new file mode 100644 index 0000000000000..46b2420af43f6 --- /dev/null +++ b/clang/test/Sema/reflection-meta-info.pass.cpp @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only +// RUN: %clang_cc1 %s -std=c++26 -freflection -fexperimental-new-constant-interpreter -fsyntax-only + +typedef int int32_t; +using A = int; + +using info = decltype(^^int); + +template <auto R> +consteval auto f1() { + return R; +} + +template <decltype(^^int) R> +consteval auto f2() { + return R; +} + +consteval void test() +{ + constexpr auto r = ^^int; + constexpr auto q = ^^float; + constexpr info v{}; + + static_assert(__is_same(decltype(^^int), info)); + static_assert(__is_same(decltype(^^float), info)); + static_assert(__is_same(decltype(^^double), info)); + static_assert(__is_same(decltype(^^long), info)); + static_assert(__is_same(decltype(^^long long), info)); + static_assert(__is_same(decltype(^^short), info)); + static_assert(__is_same(decltype(^^char), info)); + static_assert(__is_same(decltype(^^unsigned char), info)); + static_assert(__is_same(decltype(^^unsigned short), info)); + static_assert(__is_same(decltype(^^unsigned int), info)); + static_assert(__is_same(decltype(^^unsigned long), info)); + static_assert(__is_same(decltype(^^unsigned long long), info)); + static_assert(__is_same(decltype(^^info), info)); + + static_assert(__is_same(decltype(^^int), decltype(^^int))); + static_assert(__is_same(decltype(^^int), decltype(^^float))); + static_assert(__is_same(decltype(^^int), decltype(^^char))); + static_assert(__is_same(decltype(^^double), decltype(^^float))); + + static_assert(!__is_same(decltype(^^int), int)); + static_assert(__is_scalar(info)); + + static_assert(f1< ^^int >() == ^^int); + static_assert(f1< ^^A >() != ^^int); + static_assert(f1< ^^float>() != ^^int); + + static_assert(f2<r>() == ^^int); + static_assert(f2<^^float>() != ^^int); + + static_assert(sizeof(info) == 8); + static_assert(alignof(info) == 1); + static_assert(sizeof(decltype(^^int)) == sizeof(decltype(^^float))); + static_assert(^^int32_t != ^^int); + static_assert(^^const int != ^^int); + static_assert(^^volatile int32_t == ^^volatile int); + static_assert(^^const volatile int32_t == ^^const volatile int); + static_assert(^^A != ^^int); + + + static_assert(^^int == ^^int); + static_assert(^^int != ^^float); + static_assert(^^float != ^^int); + static_assert(!(^^float == ^^int)); + static_assert(r != q); + + int a; + static_assert(^^int == ^^decltype(a)); + + using foo = const int; + static_assert(^^foo != ^^const int); + static_assert(^^const foo == ^^const int); + static_assert(^^const int == ^^int const); +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 6e30ffd6b322d..bf641c27f8c1c 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1563,6 +1563,7 @@ bool CursorVisitor::VisitBuiltinTypeLoc(BuiltinTypeLoc TL) { case BuiltinType::Void: case BuiltinType::NullPtr: + case BuiltinType::MetaInfo: case BuiltinType::Dependent: #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ case BuiltinType::Id: diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 503f5da8af90f..592974804aad5 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -696,6 +696,32 @@ TEST_P(ImportType, ImportUsingType) { cxxNewExpr(hasType(pointerType(pointee(usingType()))))))); } +struct ImportReflection : ASTImporterOptionSpecificTestBase { + std::vector<std::string> getExtraArgs() const override { + return {"-Xclang", "-freflection"}; + } +}; + +TEST_P(ImportReflection, ImportMetaInfoBuiltinType) { + Decl *FromTU = getTuDecl("using declToImport = decltype(^^int);", Lang_CXX26, + "input.cc"); + auto *FromTA = FirstDeclMatcher<TypeAliasDecl>().match( + FromTU, typeAliasDecl(hasName("declToImport"))); + ASSERT_TRUE(FromTA); + + const auto *FromBT = + FromTA->getUnderlyingType().getCanonicalType()->getAs<BuiltinType>(); + ASSERT_TRUE(FromBT); + ASSERT_EQ(BuiltinType::MetaInfo, FromBT->getKind()); + + auto *ToTA = Import(FromTA, Lang_CXX26); + ASSERT_TRUE(ToTA); + const auto *ToBT = + ToTA->getUnderlyingType().getCanonicalType()->getAs<BuiltinType>(); + ASSERT_TRUE(ToBT); + EXPECT_EQ(BuiltinType::MetaInfo, ToBT->getKind()); +} + TEST_P(ImportDecl, ImportFunctionTemplateDecl) { MatchVerifier<Decl> Verifier; testImport("template <typename T> void declToImport() { };", Lang_CXX03, "", @@ -10873,6 +10899,9 @@ INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportInjectedClassNameType, INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportMatrixType, DefaultTestValuesForRunOptions); +INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportReflection, + DefaultTestValuesForRunOptions); + INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportTemplateParmDeclDefaultValue, DefaultTestValuesForRunOptions); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 2bfa187c009a7..07d08e181eae0 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -4904,6 +4904,9 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type) { case clang::BuiltinType::NullPtr: return lldb::eEncodingUint; + case clang::BuiltinType::MetaInfo: + return lldb::eEncodingUint; + case clang::BuiltinType::Kind::ARCUnbridgedCast: case clang::BuiltinType::Kind::BoundMember: case clang::BuiltinType::Kind::BuiltinFn: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
