Instantiating a class template should not instantiate the definition of any
scoped enumeration members. Later uses of an enumeration temploid as a nested
name specifier should cause its instantiation. Plus some groundwork for
explicit specialization of member enumerations of class templates.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152750 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 1c414dd..5e6c27b 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Sema/Template.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ExprCXX.h"
@@ -209,43 +210,52 @@
                                       DeclContext *DC) {
   assert(DC != 0 && "given null context");
 
-  if (TagDecl *tag = dyn_cast<TagDecl>(DC)) {
-    // If this is a dependent type, then we consider it complete.
-    if (tag->isDependentContext())
-      return false;
+  TagDecl *tag = dyn_cast<TagDecl>(DC);
 
-    // If we're currently defining this type, then lookup into the
-    // type is okay: don't complain that it isn't complete yet.
-    QualType type = Context.getTypeDeclType(tag);
-    const TagType *tagType = type->getAs<TagType>();
-    if (tagType && tagType->isBeingDefined())
-      return false;
+  // If this is a dependent type, then we consider it complete.
+  if (!tag || tag->isDependentContext())
+    return false;
 
-    SourceLocation loc = SS.getLastQualifierNameLoc();
-    if (loc.isInvalid()) loc = SS.getRange().getBegin();
+  // If we're currently defining this type, then lookup into the
+  // type is okay: don't complain that it isn't complete yet.
+  QualType type = Context.getTypeDeclType(tag);
+  const TagType *tagType = type->getAs<TagType>();
+  if (tagType && tagType->isBeingDefined())
+    return false;
 
-    // The type must be complete.
-    if (RequireCompleteType(loc, type,
-                            PDiag(diag::err_incomplete_nested_name_spec)
-                              << SS.getRange())) {
-      SS.SetInvalid(SS.getRange());
-      return true;
-    }
+  SourceLocation loc = SS.getLastQualifierNameLoc();
+  if (loc.isInvalid()) loc = SS.getRange().getBegin();
 
-    // Fixed enum types are complete, but they aren't valid as scopes
-    // until we see a definition, so awkwardly pull out this special
-    // case.
-    if (const EnumType *enumType = dyn_cast_or_null<EnumType>(tagType)) {
-      if (!enumType->getDecl()->isCompleteDefinition()) {
-        Diag(loc, diag::err_incomplete_nested_name_spec)
-          << type << SS.getRange();
-        SS.SetInvalid(SS.getRange());
-        return true;
-      }
-    }
+  // The type must be complete.
+  if (RequireCompleteType(loc, type,
+                          PDiag(diag::err_incomplete_nested_name_spec)
+                            << SS.getRange())) {
+    SS.SetInvalid(SS.getRange());
+    return true;
   }
 
-  return false;
+  // Fixed enum types are complete, but they aren't valid as scopes
+  // until we see a definition, so awkwardly pull out this special
+  // case.
+  const EnumType *enumType = dyn_cast_or_null<EnumType>(tagType);
+  if (!enumType || enumType->getDecl()->isCompleteDefinition())
+    return false;
+
+  // Try to instantiate the definition, if this is a specialization of an
+  // enumeration temploid.
+  EnumDecl *ED = enumType->getDecl();
+  if (EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {
+    MemberSpecializationInfo *MSI = ED->getMemberSpecializationInfo();
+    if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+      return InstantiateEnum(loc, ED, Pattern,
+                             getTemplateInstantiationArgs(ED),
+                             TSK_ImplicitInstantiation);
+  }
+
+  Diag(loc, diag::err_incomplete_nested_name_spec)
+    << type << SS.getRange();
+  SS.SetInvalid(SS.getRange());
+  return true;
 }
 
 bool Sema::ActOnCXXGlobalScopeSpecifier(Scope *S, SourceLocation CCLoc,
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c1ef732..3363af3 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -7738,6 +7738,50 @@
 }
 
 
+/// \brief Check that this is a valid underlying type for an enum declaration.
+bool Sema::CheckEnumUnderlyingType(TypeSourceInfo *TI) {
+  SourceLocation UnderlyingLoc = TI->getTypeLoc().getBeginLoc();
+  QualType T = TI->getType();
+
+  if (T->isDependentType() || T->isIntegralType(Context))
+    return false;
+
+  Diag(UnderlyingLoc, diag::err_enum_invalid_underlying) << T;
+  return true;
+}
+
+/// Check whether this is a valid redeclaration of a previous enumeration.
+/// \return true if the redeclaration was invalid.
+bool Sema::CheckEnumRedeclaration(SourceLocation EnumLoc, bool IsScoped,
+                                  QualType EnumUnderlyingTy,
+                                  const EnumDecl *Prev) {
+  bool IsFixed = !EnumUnderlyingTy.isNull();
+
+  if (IsScoped != Prev->isScoped()) {
+    Diag(EnumLoc, diag::err_enum_redeclare_scoped_mismatch)
+      << Prev->isScoped();
+    Diag(Prev->getLocation(), diag::note_previous_use);
+    return true;
+  }
+
+  if (IsFixed && Prev->isFixed()) {
+    if (!Context.hasSameUnqualifiedType(EnumUnderlyingTy,
+                                        Prev->getIntegerType())) {
+      Diag(EnumLoc, diag::err_enum_redeclare_type_mismatch)
+        << EnumUnderlyingTy << Prev->getIntegerType();
+      Diag(Prev->getLocation(), diag::note_previous_use);
+      return true;
+    }
+  } else if (IsFixed != Prev->isFixed()) {
+    Diag(EnumLoc, diag::err_enum_redeclare_fixed_mismatch)
+      << Prev->isFixed();
+    Diag(Prev->getLocation(), diag::note_previous_use);
+    return true;
+  }
+
+  return false;
+}
+
 /// \brief Determine whether a tag with a given kind is acceptable
 /// as a redeclaration of the given tag declaration.
 ///
@@ -7913,16 +7957,11 @@
       QualType T = GetTypeFromParser(UnderlyingType.get(), &TI);
       EnumUnderlying = TI;
 
-      SourceLocation UnderlyingLoc = TI->getTypeLoc().getBeginLoc();
-
-      if (!T->isDependentType() && !T->isIntegralType(Context)) {
-        Diag(UnderlyingLoc, diag::err_enum_invalid_underlying)
-          << T;
+      if (CheckEnumUnderlyingType(TI))
         // Recover by falling back to int.
         EnumUnderlying = Context.IntTy.getTypePtr();
-      }
 
-      if (DiagnoseUnexpandedParameterPack(UnderlyingLoc, TI, 
+      if (DiagnoseUnexpandedParameterPack(TI->getTypeLoc().getBeginLoc(), TI,
                                           UPPC_FixedUnderlyingType))
         EnumUnderlying = Context.IntTy.getTypePtr();
 
@@ -8196,37 +8235,17 @@
             return PrevTagDecl;
           }
 
+          QualType EnumUnderlyingTy;
+          if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast<TypeSourceInfo*>())
+            EnumUnderlyingTy = TI->getType();
+          else if (const Type *T = EnumUnderlying.dyn_cast<const Type*>())
+            EnumUnderlyingTy = QualType(T, 0);
+
           // All conflicts with previous declarations are recovered by
           // returning the previous declaration.
-          if (ScopedEnum != PrevEnum->isScoped()) {
-            Diag(KWLoc, diag::err_enum_redeclare_scoped_mismatch)
-              << PrevEnum->isScoped();
-            Diag(PrevTagDecl->getLocation(), diag::note_previous_use);
+          if (CheckEnumRedeclaration(NameLoc.isValid() ? NameLoc : KWLoc,
+                                     ScopedEnum, EnumUnderlyingTy, PrevEnum))
             return PrevTagDecl;
-          }
-          else if (EnumUnderlying && PrevEnum->isFixed()) {
-            QualType T;
-            if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast<TypeSourceInfo*>())
-                T = TI->getType();
-            else
-                T = QualType(EnumUnderlying.get<const Type*>(), 0);
-
-            if (!Context.hasSameUnqualifiedType(T, 
-                                                PrevEnum->getIntegerType())) {
-              Diag(NameLoc.isValid() ? NameLoc : KWLoc, 
-                   diag::err_enum_redeclare_type_mismatch)
-                << T
-                << PrevEnum->getIntegerType();
-              Diag(PrevTagDecl->getLocation(), diag::note_previous_use);
-              return PrevTagDecl;
-            }
-          }
-          else if (!EnumUnderlying.isNull() != PrevEnum->isFixed()) {
-            Diag(KWLoc, diag::err_enum_redeclare_fixed_mismatch)
-              << PrevEnum->isFixed();
-            Diag(PrevTagDecl->getLocation(), diag::note_previous_use);
-            return PrevTagDecl;
-          }
         }
 
         if (!Invalid) {
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 697ac0e..c2ebbf4 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -469,6 +469,11 @@
                      diag::note_template_static_data_member_def_here)
           << VD
           << Active->InstantiationRange;
+      } else if (EnumDecl *ED = dyn_cast<EnumDecl>(D)) {
+        Diags.Report(Active->PointOfInstantiation,
+                     diag::note_template_enum_def_here)
+          << ED
+          << Active->InstantiationRange;
       } else {
         Diags.Report(Active->PointOfInstantiation,
                      diag::note_template_type_alias_instantiation_here)
@@ -1680,6 +1685,51 @@
   }
 }
 
+/// Determine whether we would be unable to instantiate this template (because
+/// it either has no definition, or is in the process of being instantiated).
+static bool DiagnoseUninstantiableTemplate(Sema &S,
+                                           SourceLocation PointOfInstantiation,
+                                           TagDecl *Instantiation,
+                                           bool InstantiatedFromMember,
+                                           TagDecl *Pattern,
+                                           TagDecl *PatternDef,
+                                           TemplateSpecializationKind TSK,
+                                           bool Complain = true) {
+  if (PatternDef && !PatternDef->isBeingDefined())
+    return false;
+
+  if (!Complain || (PatternDef && PatternDef->isInvalidDecl())) {
+    // Say nothing
+  } else if (PatternDef) {
+    assert(PatternDef->isBeingDefined());
+    S.Diag(PointOfInstantiation,
+           diag::err_template_instantiate_within_definition)
+      << (TSK != TSK_ImplicitInstantiation)
+      << S.Context.getTypeDeclType(Instantiation);
+    // Not much point in noting the template declaration here, since
+    // we're lexically inside it.
+    Instantiation->setInvalidDecl();
+  } else if (InstantiatedFromMember) {
+    S.Diag(PointOfInstantiation,
+           diag::err_implicit_instantiate_member_undefined)
+      << S.Context.getTypeDeclType(Instantiation);
+    S.Diag(Pattern->getLocation(), diag::note_member_of_template_here);
+  } else {
+    S.Diag(PointOfInstantiation, diag::err_template_instantiate_undefined)
+      << (TSK != TSK_ImplicitInstantiation)
+      << S.Context.getTypeDeclType(Instantiation);
+    S.Diag(Pattern->getLocation(), diag::note_template_decl_here);
+  }
+
+  // In general, Instantiation isn't marked invalid to get more than one
+  // error for multiple undefined instantiations. But the code that does
+  // explicit declaration -> explicit definition conversion can't handle
+  // invalid declarations, so mark as invalid in that case.
+  if (TSK == TSK_ExplicitInstantiationDeclaration)
+    Instantiation->setInvalidDecl();
+  return true;
+}
+
 /// \brief Instantiate the definition of a class from a given pattern.
 ///
 /// \param PointOfInstantiation The point of instantiation within the
@@ -1712,38 +1762,10 @@
 
   CXXRecordDecl *PatternDef
     = cast_or_null<CXXRecordDecl>(Pattern->getDefinition());
-  if (!PatternDef || PatternDef->isBeingDefined()) {
-    if (!Complain || (PatternDef && PatternDef->isInvalidDecl())) {
-      // Say nothing
-    } else if (PatternDef) {
-      assert(PatternDef->isBeingDefined());
-      Diag(PointOfInstantiation,
-           diag::err_template_instantiate_within_definition)
-        << (TSK != TSK_ImplicitInstantiation)
-        << Context.getTypeDeclType(Instantiation);
-      // Not much point in noting the template declaration here, since
-      // we're lexically inside it.
-      Instantiation->setInvalidDecl();
-    } else if (Pattern == Instantiation->getInstantiatedFromMemberClass()) {
-      Diag(PointOfInstantiation,
-           diag::err_implicit_instantiate_member_undefined)
-        << Context.getTypeDeclType(Instantiation);
-      Diag(Pattern->getLocation(), diag::note_member_of_template_here);
-    } else {
-      Diag(PointOfInstantiation, diag::err_template_instantiate_undefined)
-        << (TSK != TSK_ImplicitInstantiation)
-        << Context.getTypeDeclType(Instantiation);
-      Diag(Pattern->getLocation(), diag::note_template_decl_here);
-    }
-
-    // In general, Instantiation isn't marked invalid to get more than one
-    // error for multiple undefined instantiations. But the code that does
-    // explicit declaration -> explicit definition conversion can't handle
-    // invalid declarations, so mark as invalid in that case.
-    if (TSK == TSK_ExplicitInstantiationDeclaration)
-       Instantiation->setInvalidDecl();
+  if (DiagnoseUninstantiableTemplate(*this, PointOfInstantiation, Instantiation,
+                                Instantiation->getInstantiatedFromMemberClass(),
+                                     Pattern, PatternDef, TSK, Complain))
     return true;
-  }
   Pattern = PatternDef;
 
   // \brief Record the point of instantiation.
@@ -1911,6 +1933,63 @@
   return Invalid;
 }
 
+/// \brief Instantiate the definition of an enum from a given pattern.
+///
+/// \param PointOfInstantiation The point of instantiation within the
+///        source code.
+/// \param Instantiation is the declaration whose definition is being
+///        instantiated. This will be a member enumeration of a class
+///        temploid specialization, or a local enumeration within a
+///        function temploid specialization.
+/// \param Pattern The templated declaration from which the instantiation
+///        occurs.
+/// \param TemplateArgs The template arguments to be substituted into
+///        the pattern.
+/// \param TSK The kind of implicit or explicit instantiation to perform.
+///
+/// \return \c true if an error occurred, \c false otherwise.
+bool Sema::InstantiateEnum(SourceLocation PointOfInstantiation,
+                           EnumDecl *Instantiation, EnumDecl *Pattern,
+                           const MultiLevelTemplateArgumentList &TemplateArgs,
+                           TemplateSpecializationKind TSK) {
+  EnumDecl *PatternDef = Pattern->getDefinition();
+  if (DiagnoseUninstantiableTemplate(*this, PointOfInstantiation, Instantiation,
+                                 Instantiation->getInstantiatedFromMemberEnum(),
+                                     Pattern, PatternDef, TSK,/*Complain*/true))
+    return true;
+  Pattern = PatternDef;
+
+  // Record the point of instantiation.
+  if (MemberSpecializationInfo *MSInfo
+        = Instantiation->getMemberSpecializationInfo()) {
+    MSInfo->setTemplateSpecializationKind(TSK);
+    MSInfo->setPointOfInstantiation(PointOfInstantiation);
+  }
+
+  InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
+  if (Inst)
+    return true;
+
+  // Enter the scope of this instantiation. We don't use
+  // PushDeclContext because we don't have a scope.
+  ContextRAII SavedContext(*this, Instantiation);
+  EnterExpressionEvaluationContext EvalContext(*this,
+                                               Sema::PotentiallyEvaluated);
+
+  LocalInstantiationScope Scope(*this, /*MergeWithParentScope*/true);
+
+  // Pull attributes from the pattern onto the instantiation.
+  InstantiateAttrs(TemplateArgs, Pattern, Instantiation);
+
+  TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs);
+  Instantiator.InstantiateEnumDefinition(Instantiation, Pattern);
+
+  // Exit the scope of this instantiation.
+  SavedContext.pop();
+
+  return Instantiation->isInvalidDecl();
+}
+
 namespace {
   /// \brief A partial specialization whose template arguments have matched
   /// a given template-id.
@@ -2231,6 +2310,36 @@
       if (Pattern)
         InstantiateClassMembers(PointOfInstantiation, Pattern, TemplateArgs, 
                                 TSK);
+    } else if (EnumDecl *Enum = dyn_cast<EnumDecl>(*D)) {
+      MemberSpecializationInfo *MSInfo = Enum->getMemberSpecializationInfo();
+      assert(MSInfo && "No member specialization information?");
+
+      if (MSInfo->getTemplateSpecializationKind()
+            == TSK_ExplicitSpecialization)
+        continue;
+
+      if (CheckSpecializationInstantiationRedecl(
+            PointOfInstantiation, TSK, Enum,
+            MSInfo->getTemplateSpecializationKind(),
+            MSInfo->getPointOfInstantiation(), SuppressNew) ||
+          SuppressNew)
+        continue;
+
+      if (Enum->getDefinition())
+        continue;
+
+      EnumDecl *Pattern = Enum->getInstantiatedFromMemberEnum();
+      assert(Pattern && "Missing instantiated-from-template information");
+
+      if (TSK == TSK_ExplicitInstantiationDefinition) {
+        if (!Pattern->getDefinition())
+          continue;
+
+        InstantiateEnum(PointOfInstantiation, Enum, Pattern, TemplateArgs, TSK);
+      } else {
+        MSInfo->setTemplateSpecializationKind(TSK);
+        MSInfo->setPointOfInstantiation(PointOfInstantiation);
+      }
     }
   }
 }
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index d7da736..d0ba4db 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -563,20 +563,18 @@
                                     /*PrevDecl=*/0, D->isScoped(),
                                     D->isScopedUsingClassTag(), D->isFixed());
   if (D->isFixed()) {
-    if (TypeSourceInfo* TI = D->getIntegerTypeSourceInfo()) {
+    if (TypeSourceInfo *TI = D->getIntegerTypeSourceInfo()) {
       // If we have type source information for the underlying type, it means it
       // has been explicitly set by the user. Perform substitution on it before
       // moving on.
       SourceLocation UnderlyingLoc = TI->getTypeLoc().getBeginLoc();
-      Enum->setIntegerTypeSourceInfo(SemaRef.SubstType(TI,
-                                                       TemplateArgs,
-                                                       UnderlyingLoc,
-                                                       DeclarationName()));
-
-      if (!Enum->getIntegerTypeSourceInfo())
+      TypeSourceInfo *NewTI = SemaRef.SubstType(TI, TemplateArgs, UnderlyingLoc,
+                                                DeclarationName());
+      if (!NewTI || SemaRef.CheckEnumUnderlyingType(NewTI))
         Enum->setIntegerType(SemaRef.Context.IntTy);
-    }
-    else {
+      else
+        Enum->setIntegerTypeSourceInfo(NewTI);
+    } else {
       assert(!D->getIntegerType()->isDependentType()
              && "Dependent type without type source info");
       Enum->setIntegerType(D->getIntegerType());
@@ -585,20 +583,38 @@
 
   SemaRef.InstantiateAttrs(TemplateArgs, D, Enum);
 
-  Enum->setInstantiationOfMemberEnum(D);
+  Enum->setInstantiationOfMemberEnum(D, TSK_ImplicitInstantiation);
   Enum->setAccess(D->getAccess());
   if (SubstQualifier(D, Enum)) return 0;
   Owner->addDecl(Enum);
-  Enum->startDefinition();
+
+  // FIXME: If this is a redeclaration:
+  // CheckEnumRedeclaration(Enum->getLocation(), Enum->isScoped(),
+  //                        Enum->getIntegerType(), Prev);
 
   if (D->getDeclContext()->isFunctionOrMethod())
     SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum);
 
+  // C++11 [temp.inst]p1: The implicit instantiation of a class template
+  // specialization causes the implicit instantiation of the declarations, but
+  // not the definitions of scoped member enumerations.
+  // FIXME: There appears to be no wording for what happens for an enum defined
+  // within a block scope, but we treat that like a member of a class template.
+  if (!Enum->isScoped())
+    InstantiateEnumDefinition(Enum, D);
+
+  return Enum;
+}
+
+void TemplateDeclInstantiator::InstantiateEnumDefinition(
+    EnumDecl *Enum, EnumDecl *Pattern) {
+  Enum->startDefinition();
+
   SmallVector<Decl*, 4> Enumerators;
 
   EnumConstantDecl *LastEnumConst = 0;
-  for (EnumDecl::enumerator_iterator EC = D->enumerator_begin(),
-         ECEnd = D->enumerator_end();
+  for (EnumDecl::enumerator_iterator EC = Pattern->enumerator_begin(),
+         ECEnd = Pattern->enumerator_end();
        EC != ECEnd; ++EC) {
     // The specified value for the enumerator.
     ExprResult Value = SemaRef.Owned((Expr *)0);
@@ -636,7 +652,8 @@
       Enumerators.push_back(EnumConst);
       LastEnumConst = EnumConst;
 
-      if (D->getDeclContext()->isFunctionOrMethod()) {
+      if (Pattern->getDeclContext()->isFunctionOrMethod() &&
+          !Enum->isScoped()) {
         // If the enumeration is within a function or method, record the enum
         // constant as a local.
         SemaRef.CurrentInstantiationScope->InstantiatedLocal(*EC, EnumConst);
@@ -644,14 +661,11 @@
     }
   }
 
-  // FIXME: Fixup LBraceLoc and RBraceLoc
-  // FIXME: Empty Scope and AttributeList (required to handle attribute packed).
-  SemaRef.ActOnEnumBody(Enum->getLocation(), SourceLocation(), SourceLocation(),
-                        Enum,
+  // FIXME: Fixup LBraceLoc
+  SemaRef.ActOnEnumBody(Enum->getLocation(), SourceLocation(),
+                        Enum->getRBraceLoc(), Enum,
                         Enumerators.data(), Enumerators.size(),
                         0, 0);
-
-  return Enum;
 }
 
 Decl *TemplateDeclInstantiator::VisitEnumConstantDecl(EnumConstantDecl *D) {