From dcc8c346d47949768b6b233cf16f7c40cfaf1fd8 Mon Sep 17 00:00:00 2001 From: Dan Katz Date: Mon, 12 Aug 2024 16:49:04 -0400 Subject: [PATCH] Initial support for importing reflections between modules. Some work remains: In particular, if this is going to "work" (i.e., supported by P2996), we need to think carefully about reachability, TU-local entities, etc. There probably need to be some constraints around use of imported reflections, and possibly some 'is_reachable' metafunction. Not entirely sure - need to experiment further. Closes issue #4. --- clang/include/clang/AST/ExprCXX.h | 149 ++++++++-------- clang/include/clang/AST/PropertiesBase.td | 160 +++++++++++++++++- clang/include/clang/Sema/Sema.h | 1 + .../include/clang/Serialization/ASTBitCodes.h | 2 +- .../clang/Serialization/ASTRecordReader.h | 8 + .../clang/Serialization/ASTRecordWriter.h | 4 + clang/lib/AST/ExprCXX.cpp | 48 +++++- clang/lib/CodeGen/CodeGenModule.cpp | 6 +- clang/lib/Sema/SemaReflect.cpp | 35 ++-- clang/lib/Serialization/ASTReaderStmt.cpp | 68 +++++++- clang/lib/Serialization/ASTWriterStmt.cpp | 41 ++++- .../reflection/example-module.cppm | 75 ++++++++ .../reflection/module-imports.sh.cpp | 96 +++++++++++ 13 files changed, 585 insertions(+), 108 deletions(-) create mode 100644 libcxx/test/std/experimental/reflection/example-module.cppm create mode 100644 libcxx/test/std/experimental/reflection/module-imports.sh.cpp diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index cc8a38e1562ef2..a7fa121457e936 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5323,6 +5323,7 @@ class BuiltinBitCastExpr final /// is either a type, an expression, a template-name, or a namespace. class CXXReflectExpr : public Expr { enum class OperandKind { + Unset, Reflection, DependentExpr }; @@ -5337,12 +5338,14 @@ class CXXReflectExpr : public Expr { CXXReflectExpr(const ASTContext &C, QualType ExprTy, APValue RV); CXXReflectExpr(const ASTContext &C, QualType ExprTy, Expr *DepSubExpr); + CXXReflectExpr(EmptyShell Empty); public: static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, SourceRange OperandRange, APValue RV); static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc, Expr *DepSubExpr); + static CXXReflectExpr *CreateEmpty(const ASTContext &C); /// Returns the operand of the reflection expression. APValue getReflection() const { @@ -5369,6 +5372,20 @@ class CXXReflectExpr : public Expr { SourceLocation getOperatorLoc() const { return OperatorLoc; } SourceRange getOperandRange() const { return OperandRange; } + /// Sets the APValue operand. + void setAPValue(APValue RV) { + assert(Kind == OperandKind::Unset || Kind == OperandKind::Reflection); + Kind = OperandKind::Reflection; + new ((void *)(char *)&Operand) APValue(RV); + } + + /// Sets the dependent subexpression operand. + void setDependentSubExpr(Expr *E) { + assert(Kind == OperandKind::Unset || Kind == OperandKind::DependentExpr); + Kind = OperandKind::DependentExpr; + new ((void *)(char *)&Operand) Expr *(E); + } + /// Sets the location of the '^'-operator. void setOperatorLoc(SourceLocation L) { OperatorLoc = L; } void setOperandRange(SourceRange R) { OperandRange = R; } @@ -5412,7 +5429,7 @@ class CXXMetafunctionExpr : public Expr { // An unowned reference to a callback for executing the metafunction at // constant evaluation time. - const ImplFn &Impl; + const ImplFn *Impl; // Result type. QualType ResultType; @@ -5430,6 +5447,9 @@ class CXXMetafunctionExpr : public Expr { QualType ResultType, ExprValueKind VK, Expr ** Args, unsigned NumArgs, SourceLocation KwLoc, SourceLocation LParenLoc, SourceLocation RParenLoc); + + CXXMetafunctionExpr(EmptyShell Empty); + public: static CXXMetafunctionExpr *Create(ASTContext &C, unsigned MetaFnID, const ImplFn &Impl, @@ -5439,46 +5459,40 @@ class CXXMetafunctionExpr : public Expr { SourceLocation LParenLoc, SourceLocation RParenLoc); - unsigned getMetaFnID() const { - return MetaFnID; - } + static CXXMetafunctionExpr *CreateEmpty(ASTContext &C); - const ImplFn &getImpl() const { - return Impl; - } + unsigned getMetaFnID() const { return MetaFnID; } + void setMetaFnID(unsigned ID) { MetaFnID = ID; } - QualType getResultType() const { - return ResultType; - } + const ImplFn &getImpl() const { return *Impl; } + void setImpl(const ImplFn &Fn) { Impl = &Fn; } - unsigned getNumArgs() const { - return NumArgs; - } + QualType getResultType() const { return ResultType; } + void setResultType(QualType QT) { ResultType = QT; } + + // TODO(P2996): Consider implementing this with trailing objects. + unsigned getNumArgs() const { return NumArgs; } Expr *getArg(unsigned I) const { assert(I < NumArgs && "argument out-of-range"); return cast(Args[I]); } - - SourceLocation getKwLoc() const { - return KwLoc; + void setArgs(Expr **NewArgs, unsigned Count) { + Args = NewArgs; + NumArgs = Count; } - SourceLocation getLParenLoc() const { - return LParenLoc; - } + SourceLocation getKwLoc() const { return KwLoc; } + void setKwLoc(SourceLocation Loc) { KwLoc = Loc; } - SourceLocation getRParenLoc() const { - return RParenLoc; - } + SourceLocation getLParenLoc() const { return LParenLoc; } + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } - SourceLocation getBeginLoc() const { - return KwLoc; - } + SourceLocation getRParenLoc() const { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } - SourceLocation getEndLoc() const { - return RParenLoc; - } + SourceLocation getBeginLoc() const { return KwLoc; } + SourceLocation getEndLoc() const { return RParenLoc; } SourceRange getSourceRange() const { return SourceRange(getBeginLoc(), getEndLoc()); @@ -5511,6 +5525,8 @@ class CXXSpliceSpecifierExpr : public Expr { SourceLocation LSpliceLoc, Expr *Operand, SourceLocation RSpliceLoc); + CXXSpliceSpecifierExpr(EmptyShell Empty); + public: static CXXSpliceSpecifierExpr *Create(ASTContext &C, SourceLocation TemplateKWLoc, @@ -5518,21 +5534,19 @@ class CXXSpliceSpecifierExpr : public Expr { Expr *Operand, SourceLocation RSpliceLoc); - Expr *getOperand() const { - return Operand; - } + static CXXSpliceSpecifierExpr *CreateEmpty(ASTContext &C); - SourceLocation getTemplateKWLoc() const { - return TemplateKWLoc; - } + Expr *getOperand() const { return Operand; } + void setOperand(Expr *E) { Operand = E; } - SourceLocation getLSpliceLoc() const { - return LSpliceLoc; - } + SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; } + void setTemplateKWLoc(SourceLocation Loc) { TemplateKWLoc = Loc; } - SourceLocation getRSpliceLoc() const { - return RSpliceLoc; - } + SourceLocation getLSpliceLoc() const { return LSpliceLoc; } + void setLSpliceLoc(SourceLocation Loc) { LSpliceLoc = Loc; } + + SourceLocation getRSpliceLoc() const { return RSpliceLoc; } + void setRSpliceLoc(SourceLocation Loc) { RSpliceLoc = Loc; } SourceLocation getBeginLoc() const { if (TemplateKWLoc.isValid()) @@ -5659,6 +5673,8 @@ class CXXSpliceExpr final const TemplateArgumentListInfo *TemplateArgs, bool AllowMemberReference); + CXXSpliceExpr(EmptyShell Empty); + inline ASTTemplateKWAndArgsInfo *getTrailingASTTemplateKWAndArgsInfo() { return getTrailingObjects(); } @@ -5696,13 +5712,13 @@ class CXXSpliceExpr final const TemplateArgumentListInfo *TemplateArgs, bool AllowMemberReference); - Expr *getOperand() const { - return Operand; - } + static CXXSpliceExpr *CreateEmpty(ASTContext &C); - bool allowMemberReference() const { - return AllowMemberReference; - } + Expr *getOperand() const { return Operand; } + void setOperand(Expr *E) { Operand = E; } + + bool allowMemberReference() const { return AllowMemberReference; } + void setAllowMemberReference(bool Allow) { AllowMemberReference = Allow; } /// Determines whether the splice was preceded by the template keyword. bool hasTemplateKeyword() const { return getTemplateKeywordLoc().isValid(); } @@ -5755,13 +5771,11 @@ class CXXSpliceExpr final return getTrailingASTTemplateKWAndArgsInfo()->RAngleLoc; } - SourceLocation getLSpliceLoc() const { - return LSpliceLoc; - } + SourceLocation getLSpliceLoc() const { return LSpliceLoc; } + void setLSpliceLoc(SourceLocation Loc) { LSpliceLoc = Loc; } - SourceLocation getRSpliceLoc() const { - return RSpliceLoc; - } + SourceLocation getRSpliceLoc() const { return RSpliceLoc; } + void setRSpliceLoc(SourceLocation Loc) { RSpliceLoc = Loc; } SourceLocation getBeginLoc() const { if (SourceLocation KWLoc = getTemplateKeywordLoc(); KWLoc.isValid()) @@ -5807,34 +5821,29 @@ class CXXDependentMemberSpliceExpr : public Expr { SourceLocation OpLoc, bool IsArrow, CXXSpliceExpr *RHS); + CXXDependentMemberSpliceExpr(EmptyShell Empty); + public: static CXXDependentMemberSpliceExpr *Create(ASTContext &C, Expr *Base, SourceLocation OpLoc, bool IsArrow, CXXSpliceExpr *RHS); - Expr *getBase() const { - return cast(SubExprs[0]); - } + static CXXDependentMemberSpliceExpr *CreateEmpty(ASTContext &C); - SourceLocation getOpLoc() const { - return OpLoc; - } + Expr *getBase() const { return cast(SubExprs[0]); } + void setBase(Expr *E) { SubExprs[0] = E; } - bool isArrow() const { - return IsArrow; - } + SourceLocation getOpLoc() const { return OpLoc; } + void setOpLoc(SourceLocation Loc) { OpLoc = Loc; } - CXXSpliceExpr *getRHS() const { - return cast(SubExprs[1]); - } + bool isArrow() const { return IsArrow; } + void setIsArrow(bool Arrow) { IsArrow = Arrow; } - SourceLocation getBeginLoc() const { - return getBase()->getBeginLoc(); - } + CXXSpliceExpr *getRHS() const { return cast(SubExprs[1]); } + void setRHS(CXXSpliceExpr *E) { SubExprs[1] = E; } - SourceLocation getEndLoc() const { - return getRHS()->getEndLoc(); - } + SourceLocation getBeginLoc() const { return getBase()->getBeginLoc(); } + SourceLocation getEndLoc() const { return getRHS()->getEndLoc(); } child_range children() { return child_range(SubExprs, SubExprs + 2); diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index e189a82f54f13c..98ab7c7b0292c4 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -83,8 +83,7 @@ def Bool : PropertyType<"bool">; def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">; def BTFTypeTagAttr : PropertyType<"const BTFTypeTagAttr *">; def CallingConv : EnumPropertyType; -def CXXBaseSpecifierRef : - RefPropertyType<"CXXBaseSpecifier"> { let ConstWhenWriting = 1; } +def Char : CountPropertyType<"char">; def DeclarationName : PropertyType; def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">; def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; } @@ -143,8 +142,6 @@ def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; } def ExprRef : SubclassPropertyType<"Expr", StmtRef>; def CXXSpliceSpecifierExprRef : SubclassPropertyType<"CXXSpliceSpecifierExpr", ExprRef>; -def TagDataMemberSpecRef : - RefPropertyType<"TagDataMemberSpec"> { let ConstWhenWriting = 1; } def TemplateArgument : PropertyType; def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">; def TemplateName : DefaultValuePropertyType; @@ -436,8 +433,161 @@ let Class = PropertyTypeCase in { }]>; } let Class = PropertyTypeCase in { + def : ReadHelper<[{ + APValue base = node; + ReflectionKind baseKind = node.getReflectionKind(); + { + while (base.getReflectionDepth() > 1) + base = base.Lower(); + baseKind = base.getReflectionKind(); + + if (base.getReflectionDepth() == 1) + base = base.Lower(); + if (base.isReflection()) + baseKind = base.getReflectionKind(); + } + + size_t baseSpecIdx = 0; + if (baseKind == ReflectionKind::BaseSpecifier) { + CXXBaseSpecifier *specifier = base.getReflectedBaseSpecifier(); + for (const CXXBaseSpecifier &s : specifier->getDerived()->bases()) { + if (&s == specifier) + break; + ++baseSpecIdx; + } + + assert(baseSpecIdx < specifier->getDerived()->getNumBases()); + } + + ArrayRef baseTdmsNameArray; + if (baseKind == ReflectionKind::DataMemberSpec) { + TagDataMemberSpec *TDMS = base.getReflectedDataMemberSpec(); + if (TDMS->Name.has_value()) + baseTdmsNameArray = ArrayRef(TDMS->Name->data(), TDMS->Name->size()); + } + }]>; + + def : Property<"depth", UInt32> { + let Read = [{ node.getReflectionDepth() }]; + } + + def : Property<"reflectedResultType", QualType> { + let Conditional = [{ depth > 0 }]; + let Read = [{ node.getTypeOfReflectedResult(getASTContext()) }]; + } + + def : Property<"kind", ReflectionKind> { + let Read = [{ baseKind }]; + } + + def : Property<"baseType", QualType> { + let Conditional = [{ kind == ReflectionKind::Type }]; + let Read = [{ base.getReflectedType() }]; + } + def : Property<"baseAPValue", APValue> { + let Conditional = [{ depth > 0 }]; + let Read = [{ base }]; + } + def : Property<"baseDecl", ValueDeclRef> { + let Conditional = [{ kind == ReflectionKind::Declaration }]; + let Read = [{ base.getReflectedDecl() }]; + } + def : Property<"baseTemplate", TemplateName> { + let Conditional = [{ kind == ReflectionKind::Template }]; + let Read = [{ base.getReflectedTemplate() }]; + } + def : Property<"baseNamespace", DeclRef> { + let Conditional = [{ kind == ReflectionKind::Namespace }]; + let Read = [{ base.getReflectedNamespace() }]; + } + def : Property<"baseBaseSpecDerivedFrom", CXXRecordDeclRef> { + let Conditional = [{ kind == ReflectionKind::BaseSpecifier }]; + let Read = [{ base.getReflectedBaseSpecifier()->getDerived() }]; + } + def : Property<"baseBaseSpecIdx", UInt32> { + let Conditional = [{ kind == ReflectionKind::BaseSpecifier }]; + let Read = [{ baseSpecIdx }]; + } + + def : Property<"baseTdmsType", QualType> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ base.getReflectedDataMemberSpec()->Ty }]; + } + def : Property<"baseTdmsHasName", Bool> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ base.getReflectedDataMemberSpec()->Name.has_value() }]; + } + def : Property<"baseTdmsName", Array> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ baseTdmsNameArray }]; + } + def : Property<"baseTdmsHasAlignment", Bool> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ base.getReflectedDataMemberSpec()->Alignment.has_value() }]; + } + def : Property<"baseTdmsAlignment", UInt32> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ base.getReflectedDataMemberSpec()->Alignment.value_or(0) }]; + } + def : Property<"baseTdmsHasBitWidth", Bool> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ base.getReflectedDataMemberSpec()->BitWidth.has_value() }]; + } + def : Property<"baseTdmsBitWidth", UInt32> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ base.getReflectedDataMemberSpec()->BitWidth.value_or(0) }]; + } + def : Property<"baseTdmsNoUniqueAddress", Bool> { + let Conditional = [{ kind == ReflectionKind::DataMemberSpec }]; + let Read = [{ base.getReflectedDataMemberSpec()->NoUniqueAddress }]; + } + def : Creator<[{ - llvm_unreachable("unimplemented"); + APValue V; + switch (kind) { + case ReflectionKind::Null: + V = APValue(kind, nullptr); + break; + case ReflectionKind::Type: + V = APValue(kind, (*baseType).getAsOpaquePtr()); + break; + case ReflectionKind::Object: + case ReflectionKind::Value: + V = *baseAPValue; + break; + case ReflectionKind::Declaration: + V = APValue(kind, *baseDecl); + break; + case ReflectionKind::Template: + V = APValue(kind, (*baseTemplate).getAsVoidPointer()); + break; + case ReflectionKind::Namespace: + V = APValue(kind, *baseNamespace); + break; + case ReflectionKind::BaseSpecifier: + V = APValue( + kind, + (*baseBaseSpecDerivedFrom)->bases().begin() + (*baseBaseSpecIdx)); + break; + case ReflectionKind::DataMemberSpec: { + TagDataMemberSpec *TDMS = new (ctx) TagDataMemberSpec {}; + TDMS->Ty = *baseTdmsType; + if (*baseTdmsHasName) + TDMS->Name = std::string(baseTdmsName->begin(), baseTdmsName->end()); + if (*baseTdmsHasAlignment) + TDMS->Alignment = *baseTdmsAlignment; + if (*baseTdmsHasBitWidth) + TDMS->BitWidth = *baseTdmsBitWidth; + TDMS->NoUniqueAddress = *baseTdmsNoUniqueAddress; + + V = APValue(kind, TDMS); + } + } + + for (unsigned k = 0; k < depth; ++k) + V = V.Lift(*reflectedResultType); + + return V; }]>; } let Class = PropertyTypeCase in { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 79eb25a039325d..03ab5b98530f0b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15287,6 +15287,7 @@ class Sema final : public SemaBase { QualType ComputeResultType(QualType ExprTy, const APValue &V); + const CXXMetafunctionExpr::ImplFn &getMetafunctionCb(unsigned FnID); private: // Lambdas having bound references to this Sema object, used to evaluate diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 7c09fa10017f21..fc6f7cd80a69d5 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1992,8 +1992,8 @@ enum StmtCode { // C++2c reflection (P2996) EXPR_REFLECT, EXPR_METAFUNCTION, + EXPR_SPLICE_SPECIFIER, EXPR_SPLICE, - EXPR_EXPR_SPLICE, EXPR_DEPENDENT_MEMBER_SPLICE, EXPR_STACK_LOCATION, EXPR_EXTRACT_LVALUE, diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h index 2561418b78ca7f..d4b8e73ccff2d5 100644 --- a/clang/include/clang/Serialization/ASTRecordReader.h +++ b/clang/include/clang/Serialization/ASTRecordReader.h @@ -306,6 +306,8 @@ class ASTRecordReader /// Read a boolean value, advancing Idx. bool readBool() { return readInt() != 0; } + char readChar() { return char(readInt()); } + /// Read a 32-bit unsigned value; required to satisfy BasicReader. uint32_t readUInt32() { return uint32_t(readInt()); @@ -355,6 +357,12 @@ class ASTRecordReader SwitchCase *getSwitchCaseWithID(unsigned ID) { return Reader->getSwitchCaseWithID(ID); } + + /// P2996 hack: Use the 'Sema' object from the ASTReader to get a + /// metafunction callback during deserialization of a CXXMetafunctionExpr. + const CXXMetafunctionExpr::ImplFn &getMetafunctionCb(unsigned ID) { + return Reader->getSema()->getMetafunctionCb(ID); + } }; /// Helper class that saves the current stream position and diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h index 5e3954c0bc31ba..0c473860156b08 100644 --- a/clang/include/clang/Serialization/ASTRecordWriter.h +++ b/clang/include/clang/Serialization/ASTRecordWriter.h @@ -159,6 +159,10 @@ class ASTRecordWriter Record->push_back(Value); } + void writeChar(char Value) { + Record->push_back(Value); + } + void writeUInt32(uint32_t Value) { Record->push_back(Value); } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 38708c1eca9933..cc027c55ad41d6 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1897,10 +1897,14 @@ CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType ExprTy, APValue RV) Kind(OperandKind::Reflection) { assert(RV.isReflection()); - new ((void *)(char *)&Operand) APValue(RV); + setAPValue(RV); setDependence(computeDependence(this, C)); } +CXXReflectExpr::CXXReflectExpr(EmptyShell Empty) + : Expr(CXXReflectExprClass, Empty), Kind(OperandKind::Unset) { +} + CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType ExprTy, Expr *DepSubExpr) : Expr(CXXReflectExprClass, ExprTy, VK_PRValue, OK_Ordinary), @@ -1908,7 +1912,7 @@ CXXReflectExpr::CXXReflectExpr(const ASTContext &C, QualType ExprTy, assert(DepSubExpr->isValueDependent() && "reflection operand must be a reflection or a dependent expression"); - new ((void *)(char *)&Operand) Expr *(DepSubExpr); + setDependentSubExpr(DepSubExpr); setDependence(computeDependence(this, C)); } @@ -1930,6 +1934,10 @@ CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C, return E; } +CXXReflectExpr *CXXReflectExpr::CreateEmpty(const ASTContext &Ctx) { + return new (Ctx) CXXReflectExpr(EmptyShell()); +} + static QualType UnwrapCXXMetafunctionExprReturnType(QualType QT) { if (auto *LVRT = dyn_cast(QT)) QT = LVRT->getPointeeType(); @@ -1947,13 +1955,16 @@ CXXMetafunctionExpr::CXXMetafunctionExpr(unsigned MetaFnID, SourceLocation RParenLoc) : Expr(CXXMetafunctionExprClass, UnwrapCXXMetafunctionExprReturnType(ResultType), VK, OK_Ordinary), - MetaFnID(MetaFnID), Impl(Impl), ResultType(ResultType), + MetaFnID(MetaFnID), Impl(&Impl), ResultType(ResultType), NumArgs(NumArgs), Args(Args), KwLoc(KwLoc), LParenLoc(LParenLoc), RParenLoc(RParenLoc) { - std::copy(Args, Args + NumArgs, this->Args); setDependence(computeDependence(this)); } +CXXMetafunctionExpr::CXXMetafunctionExpr(EmptyShell Empty) + : Expr(CXXMetafunctionExprClass, Empty) { +} + CXXMetafunctionExpr *CXXMetafunctionExpr::Create(ASTContext &C, unsigned MetaFnID, const ImplFn &Impl, @@ -1971,6 +1982,10 @@ CXXMetafunctionExpr *CXXMetafunctionExpr::Create(ASTContext &C, Args.size(), KwLoc, LParenLoc, RParenLoc); } +CXXMetafunctionExpr *CXXMetafunctionExpr::CreateEmpty(ASTContext &C) { + return new (C) CXXMetafunctionExpr(EmptyShell()); +} + CXXSpliceSpecifierExpr::CXXSpliceSpecifierExpr( QualType ResultTy, SourceLocation TemplateKWLoc, SourceLocation LSpliceLoc, Expr *Operand, SourceLocation RSpliceLoc) @@ -1980,6 +1995,10 @@ CXXSpliceSpecifierExpr::CXXSpliceSpecifierExpr( setDependence(computeDependence(this)); } +CXXSpliceSpecifierExpr::CXXSpliceSpecifierExpr(EmptyShell Empty) + : Expr(CXXSpliceSpecifierExprClass, Empty) { +} + CXXSpliceSpecifierExpr *CXXSpliceSpecifierExpr::Create( ASTContext &C, SourceLocation TemplateKWLoc, SourceLocation LSpliceLoc, Expr *Operand, SourceLocation RSpliceLoc) { @@ -1987,6 +2006,10 @@ CXXSpliceSpecifierExpr *CXXSpliceSpecifierExpr::Create( LSpliceLoc, Operand, RSpliceLoc); } +CXXSpliceSpecifierExpr *CXXSpliceSpecifierExpr::CreateEmpty(ASTContext &C) { + return new (C) CXXSpliceSpecifierExpr(EmptyShell()); +} + CXXSpliceExpr::CXXSpliceExpr(QualType ResultTy, ExprValueKind ValueKind, SourceLocation TemplateKWLoc, SourceLocation LSpliceLoc, Expr *Operand, @@ -2009,6 +2032,10 @@ CXXSpliceExpr::CXXSpliceExpr(QualType ResultTy, ExprValueKind ValueKind, setDependence(computeDependence(this)); } +CXXSpliceExpr::CXXSpliceExpr(EmptyShell Empty) + : Expr(CXXSpliceExprClass, Empty) { +} + CXXSpliceExpr *CXXSpliceExpr::Create(ASTContext &C, ExprValueKind ValueKind, SourceLocation TemplateKWLoc, @@ -2031,6 +2058,10 @@ CXXSpliceExpr *CXXSpliceExpr::Create(ASTContext &C, AllowMemberReference); } +CXXSpliceExpr *CXXSpliceExpr::CreateEmpty(ASTContext &C) { + return new (C) CXXSpliceExpr(EmptyShell()); +} + StackLocationExpr::StackLocationExpr(QualType ResultTy, SourceRange Range, int FrameOffset) : Expr(StackLocationExprClass, ResultTy, VK_PRValue, OK_Ordinary), @@ -2067,6 +2098,10 @@ CXXDependentMemberSpliceExpr::CXXDependentMemberSpliceExpr( setDependence(computeDependence(this)); } +CXXDependentMemberSpliceExpr::CXXDependentMemberSpliceExpr(EmptyShell Empty) + : Expr(CXXDependentMemberSpliceExprClass, Empty) { +} + CXXDependentMemberSpliceExpr *CXXDependentMemberSpliceExpr::Create( ASTContext &C, Expr *Base, SourceLocation OpLoc, bool IsArrow, CXXSpliceExpr *RHS) { @@ -2074,6 +2109,11 @@ CXXDependentMemberSpliceExpr *CXXDependentMemberSpliceExpr::Create( IsArrow, RHS); } +CXXDependentMemberSpliceExpr *CXXDependentMemberSpliceExpr::CreateEmpty( + ASTContext &C) { + return new (C) CXXDependentMemberSpliceExpr(EmptyShell()); +} + CXXExpansionInitListExpr::CXXExpansionInitListExpr( QualType ResultTy, Expr ** SubExprs, unsigned NumSubExprs, SourceLocation LBraceLoc, SourceLocation RBraceLoc) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b801f74e68dac6..d70f8a33eacc7e 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4131,8 +4131,10 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast(GD.getDecl()); if (D->getType()->isMetaType()) { - getDiags().Report(D->getLocation(), diag::err_runtime_meta_info); - return; + if (!isa(D) && cast(D)->isConstexpr()) { + getDiags().Report(D->getLocation(), diag::err_runtime_meta_info); + return; + } } PrettyStackTraceDecl CrashInfo(const_cast(D), D->getLocation(), diff --git a/clang/lib/Sema/SemaReflect.cpp b/clang/lib/Sema/SemaReflect.cpp index 915e1221e662b5..245498723cb293 100644 --- a/clang/lib/Sema/SemaReflect.cpp +++ b/clang/lib/Sema/SemaReflect.cpp @@ -287,23 +287,34 @@ ExprResult Sema::ActOnCXXMetafunction(SourceLocation KwLoc, // Find or build a 'std::function' having a lambda with the 'Sema' object // (i.e., 'this') and the 'Metafunction' both captured. This will be provided // as a callback to evaluate the metafunction at constant evaluation time. + const auto &ImplIt = getMetafunctionCb(FnID); + + // Return the CXXMetafunctionExpr representation. + return BuildCXXMetafunctionExpr(KwLoc, LParenLoc, RParenLoc, + FnID, ImplIt, Args); +} + +const CXXMetafunctionExpr::ImplFn &Sema::getMetafunctionCb(unsigned FnID) { auto ImplIt = MetafunctionImplCbs.find(FnID); if (ImplIt == MetafunctionImplCbs.end()) { - auto MetafnImpl = - std::make_unique(std::function( - [this, Metafn]( - APValue &Result, CXXMetafunctionExpr::EvaluateFn EvalFn, - CXXMetafunctionExpr::DiagnoseFn DiagFn, QualType ResultTy, - SourceRange Range, ArrayRef Args) -> bool { - return Metafn->evaluate(Result, *this, EvalFn, DiagFn, ResultTy, - Range, Args); - })); + const Metafunction *Metafn; + Metafunction::Lookup(FnID, Metafn); + + assert(Metafn); + auto MetafnImpl = std::make_unique( + std::function( + [this, Metafn](APValue &Result, + CXXMetafunctionExpr::EvaluateFn EvalFn, + CXXMetafunctionExpr::DiagnoseFn DiagFn, + QualType ResultTy, SourceRange Range, + ArrayRef Args) -> bool { + return Metafn->evaluate(Result, *this, EvalFn, DiagFn, ResultTy, + Range, Args); + })); ImplIt = MetafunctionImplCbs.try_emplace(FnID, std::move(MetafnImpl)).first; } - // Return the CXXMetafunctionExpr representation. - return BuildCXXMetafunctionExpr(KwLoc, LParenLoc, RParenLoc, - FnID, *ImplIt->second, Args); + return *ImplIt->second; } ExprResult Sema::ActOnCXXSpliceSpecifierExpr(SourceLocation TemplateKWLoc, diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 4384bbaecf6b47..a37fe5bab94c1b 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -496,24 +496,59 @@ void ASTStmtReader::VisitCoyieldExpr(CoyieldExpr *E) { } void ASTStmtReader::VisitCXXReflectExpr(CXXReflectExpr *E) { - llvm_unreachable("unimplemented"); + VisitExpr(E); + E->setOperatorLoc(Record.readSourceLocation()); + + if (Record.readBool()) { + Expr *DepSubExpr = Record.readExpr(); + + E->setDependentSubExpr(DepSubExpr); + E->setOperandRange(DepSubExpr->getSourceRange()); + } else { + E->setAPValue(Record.readAPValue()); + E->setOperandRange(Record.readSourceRange()); + } } void ASTStmtReader::VisitCXXMetafunctionExpr(CXXMetafunctionExpr *E) { - llvm_unreachable("unimplemented"); + VisitExpr(E); + E->setKwLoc(Record.readSourceLocation()); + E->setLParenLoc(Record.readSourceLocation()); + E->setRParenLoc(Record.readSourceLocation()); + E->setMetaFnID(Record.readUInt32()); + E->setImpl(Record.getMetafunctionCb(E->getMetaFnID())); + E->setResultType(Record.readQualType()); + + unsigned NumArgs = Record.readUInt32(); + Expr **Args = new (Record.getContext()) Expr *[NumArgs]; + for (unsigned k = 0; k < NumArgs; ++k) + Args[k] = Record.readExpr(); + E->setArgs(Args, NumArgs); } void ASTStmtReader::VisitCXXSpliceSpecifierExpr(CXXSpliceSpecifierExpr *E) { - llvm_unreachable("unimplemented"); + VisitExpr(E); + E->setTemplateKWLoc(Record.readSourceLocation()); + E->setLSpliceLoc(Record.readSourceLocation()); + E->setRSpliceLoc(Record.readSourceLocation()); + E->setOperand(Record.readExpr()); } void ASTStmtReader::VisitCXXSpliceExpr(CXXSpliceExpr *E) { - llvm_unreachable("unimplemented"); + VisitExpr(E); + E->setLSpliceLoc(Record.readSourceLocation()); + E->setRSpliceLoc(Record.readSourceLocation()); + E->setAllowMemberReference(Record.readBool()); + E->setOperand(Record.readExpr()); } void ASTStmtReader::VisitCXXDependentMemberSpliceExpr( CXXDependentMemberSpliceExpr *E) { - llvm_unreachable("unimplemented"); + VisitExpr(E); + E->setOpLoc(Record.readSourceLocation()); + E->setIsArrow(Record.readBool()); + E->setBase(Record.readExpr()); + E->setRHS(cast(Record.readExpr())); } void ASTStmtReader::VisitStackLocationExpr(StackLocationExpr *E) { @@ -4341,13 +4376,34 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = OpenACCLoopConstruct::CreateEmpty(Context, NumClauses); break; } - case EXPR_REQUIRES: + case EXPR_REQUIRES: { unsigned numLocalParameters = Record[ASTStmtReader::NumExprFields]; unsigned numRequirement = Record[ASTStmtReader::NumExprFields + 1]; S = RequiresExpr::Create(Context, Empty, numLocalParameters, numRequirement); break; } + case EXPR_REFLECT: { + S = CXXReflectExpr::CreateEmpty(Context); + break; + } + case EXPR_METAFUNCTION: { + S = CXXMetafunctionExpr::CreateEmpty(Context); + break; + } + case EXPR_SPLICE_SPECIFIER: { + S = CXXSpliceSpecifierExpr::CreateEmpty(Context); + break; + } + case EXPR_SPLICE: { + S = CXXSpliceExpr::CreateEmpty(Context); + break; + } + case EXPR_DEPENDENT_MEMBER_SPLICE: { + S = CXXDependentMemberSpliceExpr::CreateEmpty(Context); + break; + } + } // We hit a STMT_STOP, so we're done with this expression. if (Finished) diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 215b0e23583ac5..af47d971983f2b 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -469,36 +469,61 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { void ASTStmtWriter::VisitCXXReflectExpr(CXXReflectExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getOperatorLoc()); - if (E->hasDependentSubExpr()) + + Record.writeBool(E->hasDependentSubExpr()); + if (E->hasDependentSubExpr()) { Record.AddStmt(E->getDependentSubExpr()); - else + } else { Record.AddAPValue(E->getReflection()); + Record.AddSourceRange(E->getOperandRange()); + } Code = serialization::EXPR_REFLECT; } void ASTStmtWriter::VisitCXXMetafunctionExpr(CXXMetafunctionExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getKwLoc()); - // TODO(P2996): Note that this cannot be safely deserialized with the current - // model. + Record.AddSourceLocation(E->getLParenLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Record.writeUInt32(E->getMetaFnID()); + Record.writeQualType(E->getResultType()); + + Record.writeUInt32(E->getNumArgs()); + for (size_t k = 0; k < E->getNumArgs(); ++k) { + Record.AddStmt(E->getArg(k)); + } + Code = serialization::EXPR_METAFUNCTION; } void ASTStmtWriter::VisitCXXSpliceSpecifierExpr(CXXSpliceSpecifierExpr *E) { VisitExpr(E); - Code = serialization::EXPR_SPLICE; + Record.AddSourceLocation(E->getTemplateKWLoc()); + Record.AddSourceLocation(E->getLSpliceLoc()); + Record.AddSourceLocation(E->getRSpliceLoc()); + Record.AddStmt(E->getOperand()); + + Code = serialization::EXPR_SPLICE_SPECIFIER; } void ASTStmtWriter::VisitCXXSpliceExpr(CXXSpliceExpr *E) { VisitExpr(E); - // TODO(P2996): Implement this. - Code = serialization::EXPR_EXPR_SPLICE; + Record.AddSourceLocation(E->getLSpliceLoc()); + Record.AddSourceLocation(E->getRSpliceLoc()); + Record.writeBool(E->allowMemberReference()); + Record.AddStmt(E->getOperand()); + + Code = serialization::EXPR_SPLICE; } void ASTStmtWriter::VisitCXXDependentMemberSpliceExpr( CXXDependentMemberSpliceExpr *E) { VisitExpr(E); - // TODO(P2996): Implement this. + Record.AddSourceLocation(E->getOpLoc()); + Record.writeBool(E->isArrow()); + Record.AddStmt(E->getBase()); + Record.AddStmt(E->getRHS()); + Code = serialization::EXPR_DEPENDENT_MEMBER_SPLICE; } diff --git a/libcxx/test/std/experimental/reflection/example-module.cppm b/libcxx/test/std/experimental/reflection/example-module.cppm new file mode 100644 index 00000000000000..c2931c435dffd0 --- /dev/null +++ b/libcxx/test/std/experimental/reflection/example-module.cppm @@ -0,0 +1,75 @@ +module; +#include + +export module Example; +namespace Example { + // ================ + // Null reflections + // ================ + +export constexpr auto rNull = decltype(^::){}; + + // =========================== + // Reflections of type aliases + // =========================== + +export using Alias = int; +export constexpr auto rAlias = ^Alias; + + // ====================== + // Reflections of objects + // ====================== + +static int obj = 13; +export constexpr auto rObj = std::meta::reflect_object(obj); + + // ===================== + // Reflections of values + // ===================== + +export constexpr auto rValue = std::meta::reflect_value(1); +export constexpr auto rRefl = std::meta::reflect_value(rValue); +export constexpr auto Splice = [:rRefl:]; + + // ======================== + // Reflections of variables + // ======================== + +export int v42 = 42; +export constexpr auto r42 = ^v42; + + // ======================== + // Reflections of templates + // ======================== + +export template int TVar = -V; +export constexpr auto rTVar = ^TVar; + +export template auto fn(const T &t) { + return t.[:M:]; +} + + // ========================= + // Reflections of namespaces + // ========================= + +export constexpr auto rGlobalNS = ^::; + + // ============================== + // Reflections of base specifiers + // ============================== +export struct Empty {}; +export struct Base { + static constexpr int K = 12; +}; +export struct Child : private Empty, Base {}; +export constexpr auto rBase1 = bases_of(^Child)[0]; +export constexpr auto rBase2 = bases_of(^Child)[1]; + + // ================================= + // Reflections of data members specs + // ================================= + +export constexpr auto rTDMS = data_member_spec(^int, {.name="test"}); + +} // namespace Example diff --git a/libcxx/test/std/experimental/reflection/module-imports.sh.cpp b/libcxx/test/std/experimental/reflection/module-imports.sh.cpp new file mode 100644 index 00000000000000..746cccbd27fc18 --- /dev/null +++ b/libcxx/test/std/experimental/reflection/module-imports.sh.cpp @@ -0,0 +1,96 @@ +// FILE_DEPENDENCIES: example-module.cppm +// +// RUN: %{cxx} %{compile_flags} -std=c++26 -freflection -fparameter-reflection \ +// RUN: --precompile example-module.cppm -o %t/example-module.pcm +// RUN: %{cxx} %{compile_flags} %{link_flags} -std=c++26 -freflection -fparameter-reflection \ +// RUN: -fmodule-file=Example=%t/example-module.pcm %t/example-module.pcm \ +// RUN: module-imports.sh.cpp -o %t/module-imports.sh.cpp.tsk +// RUN: %t/module-imports.sh.cpp.tsk > %t/stdout.txt + +// expected-no-diagnostics +#include +#include + +import Example; + + + // ================ + // Null reflections + // ================ + +static_assert(Example::rNull == std::meta::info{}); + + // ==================== + // Reflections of types + // ==================== + +static_assert(is_type(Example::rAlias)); +static_assert(is_alias(Example::rAlias)); +static_assert(dealias(Example::rAlias) == ^int); + + // ====================== + // Reflections of objects + // ====================== + +static_assert(is_object(Example::rObj)); +static_assert(type_of(Example::rObj) == ^int); + + // ===================== + // Reflections of values + // ===================== + +static_assert(is_value(Example::rValue)); +static_assert(Example::rValue == std::meta::reflect_value(1)); +static_assert(Example::rValue == [:Example::rRefl:]); +static_assert(Example::Splice == Example::rValue); + + // =========================== + // Reflections of declarations + // =========================== + +static_assert(is_variable(Example::r42)); + + // ======================== + // Reflections of templates + // ======================== + +static_assert(is_template(Example::rTVar)); + + // ========================= + // Reflections of namespaces + // ========================= + +static_assert(is_namespace(Example::rGlobalNS)); +static_assert(Example::rGlobalNS == ^::); + + // ============================== + // Reflections of base specifiers + // ============================== + +static_assert(is_private(Example::rBase1)); +static_assert(is_public(Example::rBase2)); + + // ================================ + // Reflections of data member specs + // ================================ + +static_assert(is_data_member_spec(Example::rTDMS)); +static_assert(type_of(Example::rTDMS) == ^int); + +struct S; +static_assert(is_type(define_class(^S, {Example::rTDMS}))); + + // ============== + // Driver program + // ============== + +[:Example::rAlias:] main() { + constexpr S s = {3}; + + // RUN: grep "Value: 114" %t/stdout.txt + std::println("Value: {}", + [:Example::r42:] + template [:Example::rTVar:]<2> + + [:Example::rGlobalNS:]::Example::v42 + [:Example::rValue:] + + [:Example::rObj:] + [:type_of(Example::rBase2):]::K + + s.test + Example::fn(s)); +}