From: Richard Smith Date: Tue, 15 Oct 2013 22:02:41 +0000 (+0000) Subject: C++ modules: merging for enumerations and enumerators with multiple definitions X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d692a84e18fb48e6442bee83364f1d301f5b8404;p=clang C++ modules: merging for enumerations and enumerators with multiple definitions (eg through template instantiations in multiple modules). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@192740 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 6d0a7f1730..9d2a747ca3 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -2242,7 +2242,7 @@ public: /// that is defined. For example, in "enum X {a,b}", each of a/b are /// EnumConstantDecl's, X is an instance of EnumDecl, and the type of a/b is a /// TagType for the X EnumDecl. -class EnumConstantDecl : public ValueDecl { +class EnumConstantDecl : public ValueDecl, public Mergeable { Stmt *Init; // an integer constant expression llvm::APSInt Val; // The value. protected: @@ -2268,6 +2268,14 @@ public: SourceRange getSourceRange() const LLVM_READONLY; + /// Retrieves the canonical declaration of this enumerator. + EnumConstantDecl *getCanonicalDecl() { + return getFirstDeclaration(); + } + const EnumConstantDecl *getCanonicalDecl() const { + return getFirstDeclaration(); + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == EnumConstant; } diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 66fff8f4b8..530c54e3cc 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -917,6 +917,10 @@ private: /// when merging implicit instantiations of class templates across modules. llvm::DenseMap MergedDeclContexts; + /// \brief A mapping from canonical declarations of enums to their canonical + /// definitions. Only populated when using modules in C++. + llvm::DenseMap EnumDefinitions; + /// \brief When reading a Stmt tree, Stmt operands are placed in this stack. SmallVector StmtStack; diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 2a1ac11ec0..4bfb83d00b 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -488,6 +488,19 @@ void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) { ED->IsScopedUsingClassTag = Record[Idx++]; ED->IsFixed = Record[Idx++]; + // If this is a definition subject to the ODR, and we already have a + // definition, merge this one into it. + if (ED->IsCompleteDefinition && + Reader.getContext().getLangOpts().Modules && + Reader.getContext().getLangOpts().CPlusPlus) { + if (EnumDecl *&OldDef = Reader.EnumDefinitions[ED->getCanonicalDecl()]) { + Reader.MergedDeclContexts.insert(std::make_pair(ED, OldDef)); + ED->IsCompleteDefinition = false; + } else { + OldDef = ED; + } + } + if (EnumDecl *InstED = ReadDeclAs(Record, Idx)) { TemplateSpecializationKind TSK = (TemplateSpecializationKind)Record[Idx++]; SourceLocation POI = ReadSourceLocation(Record, Idx); @@ -516,6 +529,7 @@ void ASTDeclReader::VisitEnumConstantDecl(EnumConstantDecl *ECD) { if (Record[Idx++]) ECD->setInitExpr(Reader.ReadExpr(F)); ECD->setInitVal(Reader.ReadAPSInt(Record, Idx)); + mergeMergeable(ECD); } void ASTDeclReader::VisitDeclaratorDecl(DeclaratorDecl *DD) { @@ -1893,6 +1907,12 @@ void ASTDeclReader::mergeMergeable(Mergeable *D) { if (!Reader.getContext().getLangOpts().Modules) return; + // ODR-based merging is only performed in C++. In C, identically-named things + // in different translation units are not redeclarations (but may still have + // compatible types). + if (!Reader.getContext().getLangOpts().CPlusPlus) + return; + if (FindExistingResult ExistingRes = findExisting(static_cast(D))) if (T *Existing = ExistingRes) Reader.Context.setPrimaryMergedDecl(static_cast(D), @@ -2122,9 +2142,15 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { // FIXME: Diagnose if the types don't match. More generally, diagnose if we // get a declaration in a class definition that isn't in the canonical class // definition. + // FIXME: Also check the bitwidth is odr-equivalent, if any. return X->getASTContext().hasSameType(FDX->getType(), FDY->getType()); } + // Enumerators with the same name match. + if (isa(X)) + // FIXME: Also check the value is odr-equivalent. + return true; + // FIXME: Many other cases to implement. return false; } @@ -2138,6 +2164,9 @@ static DeclContext *getPrimaryContextForMerging(DeclContext *DC) { if (CXXRecordDecl *RD = dyn_cast(DC)) return RD->getDefinition(); + if (EnumDecl *ED = dyn_cast(DC)) + return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition() : 0; + return 0; } diff --git a/test/Modules/Inputs/cxx-templates-a.h b/test/Modules/Inputs/cxx-templates-a.h index d2261f6a29..0ca6e34d0b 100644 --- a/test/Modules/Inputs/cxx-templates-a.h +++ b/test/Modules/Inputs/cxx-templates-a.h @@ -33,3 +33,10 @@ void use_some_template_a() { template struct MergeTemplates; MergeTemplates<0> *merge_templates_a; + +auto enum_a_from_a = CommonTemplate::a; +const auto enum_c_from_a = CommonTemplate::c; + +template struct UseInt; +template void UseRedeclaredEnum(UseInt::a>); +constexpr void (*UseRedeclaredEnumA)(UseInt<1>) = UseRedeclaredEnum; diff --git a/test/Modules/Inputs/cxx-templates-b.h b/test/Modules/Inputs/cxx-templates-b.h index 6cc951b0ee..f0921a7e0b 100644 --- a/test/Modules/Inputs/cxx-templates-b.h +++ b/test/Modules/Inputs/cxx-templates-b.h @@ -44,6 +44,13 @@ void use_some_template_b() { b = c; } +auto enum_b_from_b = CommonTemplate::b; +const auto enum_c_from_b = CommonTemplate::c; + +template struct UseInt; +template void UseRedeclaredEnum(UseInt::a>); +constexpr void (*UseRedeclaredEnumB)(UseInt<1>) = UseRedeclaredEnum; + @import cxx_templates_a; template void UseDefinedInBImplIndirectly(T &v) { PerformDelayedLookup(v); diff --git a/test/Modules/Inputs/cxx-templates-common.h b/test/Modules/Inputs/cxx-templates-common.h index 6993da85af..40a11e20b4 100644 --- a/test/Modules/Inputs/cxx-templates-common.h +++ b/test/Modules/Inputs/cxx-templates-common.h @@ -5,3 +5,7 @@ struct DefinedInCommon { struct Inner {}; friend void FoundByADL(DefinedInCommon); }; + +template struct CommonTemplate { + enum E { a = 1, b = 2, c = 3 }; +}; diff --git a/test/Modules/cxx-templates.cpp b/test/Modules/cxx-templates.cpp index 911f953a5c..0965aa8b0b 100644 --- a/test/Modules/cxx-templates.cpp +++ b/test/Modules/cxx-templates.cpp @@ -5,6 +5,7 @@ @import cxx_templates_a; @import cxx_templates_b; +@import cxx_templates_common; template struct Tmpl_T_C {}; template struct Tmpl_T_I_I {}; @@ -12,6 +13,8 @@ template struct Tmpl_T_I_I {}; template struct Tmpl_T_T_A {}; template struct Tmpl_T_T_B {}; +template struct UseInt {}; + void g() { f(0); f(1.0); @@ -71,6 +74,19 @@ void g() { PerformDelayedLookup(defined_in_b_impl); // expected-note {{in instantiation of}} merge_templates_a = merge_templates_b; // ok, same type + + using T = decltype(enum_a_from_a); + using T = decltype(enum_b_from_b); + T e = true ? enum_a_from_a : enum_b_from_b; + + UseRedeclaredEnum(UseInt<1>()); + // FIXME: Reintroduce this once we merge function template specializations. + //static_assert(UseRedeclaredEnumA == UseRedeclaredEnumB, ""); + //static_assert(UseRedeclaredEnumA == UseRedeclaredEnum, ""); + //static_assert(UseRedeclaredEnumB == UseRedeclaredEnum, ""); + static_assert(enum_c_from_a == enum_c_from_b, ""); + CommonTemplate cti; + CommonTemplate::E eee = CommonTemplate::c; } RedeclaredAsFriend raf1;