(eg through template instantiations in multiple modules).
llvm-svn: 192740
/// 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<EnumConstantDecl> {
Stmt *Init; // an integer constant expression
llvm::APSInt Val; // The value.
protected:
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; }
/// when merging implicit instantiations of class templates across modules.
llvm::DenseMap<DeclContext *, DeclContext *> MergedDeclContexts;
+ /// \brief A mapping from canonical declarations of enums to their canonical
+ /// definitions. Only populated when using modules in C++.
+ llvm::DenseMap<EnumDecl *, EnumDecl *> EnumDefinitions;
+
/// \brief When reading a Stmt tree, Stmt operands are placed in this stack.
SmallVector<Stmt *, 16> StmtStack;
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<EnumDecl>(Record, Idx)) {
TemplateSpecializationKind TSK = (TemplateSpecializationKind)Record[Idx++];
SourceLocation POI = ReadSourceLocation(Record, Idx);
if (Record[Idx++])
ECD->setInitExpr(Reader.ReadExpr(F));
ECD->setInitVal(Reader.ReadAPSInt(Record, Idx));
+ mergeMergeable(ECD);
}
void ASTDeclReader::VisitDeclaratorDecl(DeclaratorDecl *DD) {
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<T*>(D)))
if (T *Existing = ExistingRes)
Reader.Context.setPrimaryMergedDecl(static_cast<T*>(D),
// 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<EnumConstantDecl>(X))
+ // FIXME: Also check the value is odr-equivalent.
+ return true;
+
// FIXME: Many other cases to implement.
return false;
}
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC))
return RD->getDefinition();
+ if (EnumDecl *ED = dyn_cast<EnumDecl>(DC))
+ return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition() : 0;
+
return 0;
}
template<int> struct MergeTemplates;
MergeTemplates<0> *merge_templates_a;
+
+auto enum_a_from_a = CommonTemplate<int>::a;
+const auto enum_c_from_a = CommonTemplate<int>::c;
+
+template<int> struct UseInt;
+template<typename T> void UseRedeclaredEnum(UseInt<T() + CommonTemplate<char>::a>);
+constexpr void (*UseRedeclaredEnumA)(UseInt<1>) = UseRedeclaredEnum<int>;
b = c;
}
+auto enum_b_from_b = CommonTemplate<int>::b;
+const auto enum_c_from_b = CommonTemplate<int>::c;
+
+template<int> struct UseInt;
+template<typename T> void UseRedeclaredEnum(UseInt<T() + CommonTemplate<char>::a>);
+constexpr void (*UseRedeclaredEnumB)(UseInt<1>) = UseRedeclaredEnum<int>;
+
@import cxx_templates_a;
template<typename T> void UseDefinedInBImplIndirectly(T &v) {
PerformDelayedLookup(v);
struct Inner {};
friend void FoundByADL(DefinedInCommon);
};
+
+template<typename T> struct CommonTemplate {
+ enum E { a = 1, b = 2, c = 3 };
+};
@import cxx_templates_a;
@import cxx_templates_b;
+@import cxx_templates_common;
template<typename, char> struct Tmpl_T_C {};
template<typename, int, int> struct Tmpl_T_I_I {};
template<typename A, typename B, A> struct Tmpl_T_T_A {};
template<typename A, typename B, B> struct Tmpl_T_T_B {};
+template<int> struct UseInt {};
+
void g() {
f(0);
f<double>(1.0);
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<int>(UseInt<1>());
+ // FIXME: Reintroduce this once we merge function template specializations.
+ //static_assert(UseRedeclaredEnumA == UseRedeclaredEnumB, "");
+ //static_assert(UseRedeclaredEnumA == UseRedeclaredEnum<int>, "");
+ //static_assert(UseRedeclaredEnumB == UseRedeclaredEnum<int>, "");
+ static_assert(enum_c_from_a == enum_c_from_b, "");
+ CommonTemplate<int> cti;
+ CommonTemplate<int>::E eee = CommonTemplate<int>::c;
}
RedeclaredAsFriend<int> raf1;