From f1c66b40213784a1c4612f04c14cafa2b0e89988 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 14 Mar 2012 23:13:10 +0000 Subject: 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 --- include/clang/AST/Decl.h | 48 +++++--- include/clang/AST/DeclTemplate.h | 4 +- include/clang/Basic/DiagnosticSemaKinds.td | 2 + include/clang/Sema/Sema.h | 8 ++ include/clang/Sema/Template.h | 1 + lib/AST/Decl.cpp | 13 ++ lib/Sema/SemaCXXScopeSpec.cpp | 72 ++++++----- lib/Sema/SemaDecl.cpp | 89 ++++++++------ lib/Sema/SemaTemplateInstantiate.cpp | 171 ++++++++++++++++++++++----- lib/Sema/SemaTemplateInstantiateDecl.cpp | 54 +++++---- lib/Serialization/ASTReaderDecl.cpp | 8 +- lib/Serialization/ASTWriterDecl.cpp | 8 +- test/CXX/temp/temp.spec/temp.explicit/p8.cpp | 23 +++- test/CXX/temp/temp.spec/temp.inst/p1.cpp | 104 ++++++++++++++++ test/PCH/cxx11-enum-template.cpp | 26 ++++ www/cxx_status.html | 3 +- 16 files changed, 492 insertions(+), 142 deletions(-) create mode 100644 test/CXX/temp/temp.spec/temp.inst/p1.cpp create mode 100644 test/PCH/cxx11-enum-template.cpp diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 05be052675..a260852e8a 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -2458,7 +2458,7 @@ private: /// in the syntax of a declarator. bool IsEmbeddedInDeclarator : 1; - /// /brief True if this tag is free standing, e.g. "struct foo;". + /// \brief True if this tag is free standing, e.g. "struct foo;". bool IsFreeStanding : 1; protected: @@ -2467,7 +2467,7 @@ protected: unsigned NumNegativeBits : 8; /// IsScoped - True if this tag declaration is a scoped enumeration. Only - /// possible in C++0x mode. + /// possible in C++11 mode. bool IsScoped : 1; /// IsScopedUsingClassTag - If this tag declaration is a scoped enum, /// then this is true if the scoped enum was declared using the class @@ -2476,7 +2476,7 @@ protected: bool IsScopedUsingClassTag : 1; /// IsFixed - True if this is an enumeration with fixed underlying type. Only - /// possible in C++0x mode. + /// possible in C++11 or Microsoft extensions mode. bool IsFixed : 1; private: @@ -2671,8 +2671,9 @@ public: friend class ASTDeclWriter; }; -/// EnumDecl - Represents an enum. As an extension, we allow forward-declared -/// enums. +/// EnumDecl - Represents an enum. In C++11, enums can be forward-declared +/// with a fixed underlying type, and in C we allow them to be forward-declared +/// with no underlying type as an extension. class EnumDecl : public TagDecl { virtual void anchor(); /// IntegerType - This represent the integer type that the enum corresponds @@ -2698,10 +2699,10 @@ class EnumDecl : public TagDecl { /// in C++) are of the enum type instead. QualType PromotionType; - /// \brief If the enumeration was instantiated from an enumeration - /// within a class or function template, this pointer refers to the - /// enumeration declared within the template. - EnumDecl *InstantiatedFrom; + /// \brief If this enumeration is an instantiation of a member enumeration + /// of a class template specialization, this is the member specialization + /// information. + MemberSpecializationInfo *SpecializationInfo; // The number of positive and negative bits required by the // enumerators are stored in the SubclassBits field. @@ -2714,7 +2715,7 @@ class EnumDecl : public TagDecl { IdentifierInfo *Id, EnumDecl *PrevDecl, bool Scoped, bool ScopedUsingClassTag, bool Fixed) : TagDecl(Enum, TTK_Enum, DC, IdLoc, Id, PrevDecl, StartLoc), - InstantiatedFrom(0) { + SpecializationInfo(0) { assert(Scoped || !ScopedUsingClassTag); IntegerType = (const Type*)0; NumNegativeBits = 0; @@ -2723,6 +2724,9 @@ class EnumDecl : public TagDecl { IsScopedUsingClassTag = ScopedUsingClassTag; IsFixed = Fixed; } + + void setInstantiationOfMemberEnum(ASTContext &C, EnumDecl *ED, + TemplateSpecializationKind TSK); public: EnumDecl *getCanonicalDecl() { return cast(TagDecl::getCanonicalDecl()); @@ -2745,6 +2749,10 @@ public: return cast(TagDecl::getMostRecentDecl()); } + EnumDecl *getDefinition() const { + return cast_or_null(TagDecl::getDefinition()); + } + static EnumDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, EnumDecl *PrevDecl, @@ -2767,14 +2775,14 @@ public: typedef specific_decl_iterator enumerator_iterator; enumerator_iterator enumerator_begin() const { - const EnumDecl *E = cast_or_null(getDefinition()); + const EnumDecl *E = getDefinition(); if (!E) E = this; return enumerator_iterator(E->decls_begin()); } enumerator_iterator enumerator_end() const { - const EnumDecl *E = cast_or_null(getDefinition()); + const EnumDecl *E = getDefinition(); if (!E) E = this; return enumerator_iterator(E->decls_end()); @@ -2859,11 +2867,21 @@ public: /// \brief Returns the enumeration (declared within the template) /// from which this enumeration type was instantiated, or NULL if /// this enumeration was not instantiated from any template. - EnumDecl *getInstantiatedFromMemberEnum() const { - return InstantiatedFrom; + EnumDecl *getInstantiatedFromMemberEnum() const; + + /// \brief If this enumeration is an instantiation of a member enumeration of + /// a class template specialization, retrieves the member specialization + /// information. + MemberSpecializationInfo *getMemberSpecializationInfo() const { + return SpecializationInfo; } - void setInstantiationOfMemberEnum(EnumDecl *IF) { InstantiatedFrom = IF; } + /// \brief Specify that this enumeration is an instantiation of the + /// member enumeration ED. + void setInstantiationOfMemberEnum(EnumDecl *ED, + TemplateSpecializationKind TSK) { + setInstantiationOfMemberEnum(getASTContext(), ED, TSK); + } static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const EnumDecl *D) { return true; } diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index fc3083aa79..14a6ca685a 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -355,8 +355,8 @@ public: }; /// \brief Provides information a specialization of a member of a class -/// template, which may be a member function, static data member, or -/// member class. +/// template, which may be a member function, static data member, +/// member class or member enumeration. class MemberSpecializationInfo { // The member declaration from which this member was instantiated, and the // manner in which the instantiation occurred (in the lower two bits). diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7bbf431ae4..0670d1f31a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2529,6 +2529,8 @@ def note_function_template_spec_here : Note< "in instantiation of function template specialization %q0 requested here">; def note_template_static_data_member_def_here : Note< "in instantiation of static data member %q0 requested here">; +def note_template_enum_def_here : Note< + "in instantiation of enumeration %q0 requested here">; def note_template_type_alias_instantiation_here : Note< "in instantiation of template type alias %0 requested here">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c98efc184f..9e7f76c065 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1343,6 +1343,9 @@ public: SourceLocation IdLoc, IdentifierInfo *Id, Expr *val); + bool CheckEnumUnderlyingType(TypeSourceInfo *TI); + bool CheckEnumRedeclaration(SourceLocation EnumLoc, bool IsScoped, + QualType EnumUnderlyingTy, const EnumDecl *Prev); Decl *ActOnEnumConstant(Scope *S, Decl *EnumDecl, Decl *LastEnumConstant, SourceLocation IdLoc, IdentifierInfo *Id, @@ -5489,6 +5492,11 @@ public: TemplateSpecializationKind TSK, bool Complain = true); + bool InstantiateEnum(SourceLocation PointOfInstantiation, + EnumDecl *Instantiation, EnumDecl *Pattern, + const MultiLevelTemplateArgumentList &TemplateArgs, + TemplateSpecializationKind TSK); + struct LateInstantiatedAttribute { const Attr *TmplAttr; LocalInstantiationScope *Scope; diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index bc3eba3f2d..c16823a6a1 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -484,6 +484,7 @@ namespace clang { InstantiateClassTemplatePartialSpecialization( ClassTemplateDecl *ClassTemplate, ClassTemplatePartialSpecializationDecl *PartialSpec); + void InstantiateEnumDefinition(EnumDecl *Enum, EnumDecl *Pattern); }; } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b430fae3b1..34ec7a879b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2674,6 +2674,19 @@ void EnumDecl::completeDefinition(QualType NewType, TagDecl::completeDefinition(); } +EnumDecl *EnumDecl::getInstantiatedFromMemberEnum() const { + if (SpecializationInfo) + return cast(SpecializationInfo->getInstantiatedFrom()); + + return 0; +} + +void EnumDecl::setInstantiationOfMemberEnum(ASTContext &C, EnumDecl *ED, + TemplateSpecializationKind TSK) { + assert(!SpecializationInfo && "Member enum is already a specialization"); + SpecializationInfo = new (C) MemberSpecializationInfo(ED, TSK); +} + //===----------------------------------------------------------------------===// // RecordDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 1c414dda5d..5e6c27b15d 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 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS, DeclContext *DC) { assert(DC != 0 && "given null context"); - if (TagDecl *tag = dyn_cast(DC)) { - // If this is a dependent type, then we consider it complete. - if (tag->isDependentContext()) - return false; + TagDecl *tag = dyn_cast(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(); - 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(); + 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(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(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 c1ef732a92..3363af324c 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7738,6 +7738,50 @@ TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T, } +/// \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 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, 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 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, return PrevTagDecl; } + QualType EnumUnderlyingTy; + if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast()) + EnumUnderlyingTy = TI->getType(); + else if (const Type *T = EnumUnderlying.dyn_cast()) + 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); - return PrevTagDecl; - } - else if (EnumUnderlying && PrevEnum->isFixed()) { - QualType T; - if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast()) - T = TI->getType(); - else - T = QualType(EnumUnderlying.get(), 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); + if (CheckEnumRedeclaration(NameLoc.isValid() ? NameLoc : KWLoc, + ScopedEnum, EnumUnderlyingTy, PrevEnum)) return PrevTagDecl; - } } if (!Invalid) { diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 697ac0ecd4..c2ebbf4b55 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -469,6 +469,11 @@ void Sema::PrintInstantiationStack() { diag::note_template_static_data_member_def_here) << VD << Active->InstantiationRange; + } else if (EnumDecl *ED = dyn_cast(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 @@ namespace clang { } } +/// 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 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, CXXRecordDecl *PatternDef = cast_or_null(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 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, 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 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, if (Pattern) InstantiateClassMembers(PointOfInstantiation, Pattern, TemplateArgs, TSK); + } else if (EnumDecl *Enum = dyn_cast(*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 d7da736fe6..d0ba4db0b5 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -563,20 +563,18 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { /*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 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { 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 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 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { 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 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { } } - // 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) { diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 8a65538fce..5db5f9252b 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -453,7 +453,13 @@ void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) { ED->IsScoped = Record[Idx++]; ED->IsScopedUsingClassTag = Record[Idx++]; ED->IsFixed = Record[Idx++]; - ED->setInstantiationOfMemberEnum(ReadDeclAs(Record, Idx)); + + if (EnumDecl *InstED = ReadDeclAs(Record, Idx)) { + TemplateSpecializationKind TSK = (TemplateSpecializationKind)Record[Idx++]; + SourceLocation POI = ReadSourceLocation(Record, Idx); + ED->setInstantiationOfMemberEnum(Reader.getContext(), InstED, TSK); + ED->getMemberSpecializationInfo()->setPointOfInstantiation(POI); + } } void ASTDeclReader::VisitRecordDecl(RecordDecl *RD) { diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index a8e3c77cff..7817765caf 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -230,7 +230,13 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) { Record.push_back(D->isScoped()); Record.push_back(D->isScopedUsingClassTag()); Record.push_back(D->isFixed()); - Writer.AddDeclRef(D->getInstantiatedFromMemberEnum(), Record); + if (MemberSpecializationInfo *MemberInfo = D->getMemberSpecializationInfo()) { + Writer.AddDeclRef(MemberInfo->getInstantiatedFrom(), Record); + Record.push_back(MemberInfo->getTemplateSpecializationKind()); + Writer.AddSourceLocation(MemberInfo->getPointOfInstantiation(), Record); + } else { + Writer.AddDeclRef(0, Record); + } if (!D->hasAttrs() && !D->isImplicit() && diff --git a/test/CXX/temp/temp.spec/temp.explicit/p8.cpp b/test/CXX/temp/temp.spec/temp.explicit/p8.cpp index 0c5aec3b0b..550078ab14 100644 --- a/test/CXX/temp/temp.spec/temp.explicit/p8.cpp +++ b/test/CXX/temp/temp.spec/temp.explicit/p8.cpp @@ -1,16 +1,15 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s template struct X0 { struct MemberClass; - + T* f0(T* ptr); - + static T* static_member; }; -template class X0; // okay -template class X0; // okay; nothing gets instantiated. +template class X0; // ok; nothing gets instantiated. template struct X0::MemberClass { @@ -25,3 +24,17 @@ T* X0::f0(T* ptr) { template T* X0::static_member = 0; +template class X0; // ok + + +template +struct X1 { + enum class E { + e = T::error // expected-error 2{{no members}} + }; +}; +template struct X1; // expected-note {{here}} + +extern template struct X1; // ok + +template struct X1; // expected-note {{here}} diff --git a/test/CXX/temp/temp.spec/temp.inst/p1.cpp b/test/CXX/temp/temp.spec/temp.inst/p1.cpp new file mode 100644 index 0000000000..8684fc4dab --- /dev/null +++ b/test/CXX/temp/temp.spec/temp.inst/p1.cpp @@ -0,0 +1,104 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +// The implicit specialization of a class template specialuzation causes the +// implicit instantiation of the declarations, but not the definitions or +// default arguments, of: + +// FIXME: Many omitted cases + +// - scoped member enumerations +namespace ScopedEnum { + template struct ScopedEnum1 { + enum class E { + e = T::error // expected-error {{'double' cannot be used prior to '::'}} + }; + }; + ScopedEnum1 se1; // ok + + template struct ScopedEnum2 { + enum class E : T { // expected-error {{non-integral type 'void *' is an invalid underlying type}} + e = 0 + }; + }; + ScopedEnum2 se2; // expected-note {{here}} + + template struct UnscopedEnum3 { + enum class E : T { + e = 4 + }; + int arr[(int)E::e]; + }; + UnscopedEnum3 ue3; // ok + + ScopedEnum1::E e1; // ok + ScopedEnum1::E e2 = decltype(e2)::e; // expected-note {{in instantiation of enumeration 'ScopedEnum::ScopedEnum1::E' requested here}} + + // The behavior for enums defined within function templates is not clearly + // specified by the standard. We follow the rules for enums defined within + // class templates. + template + int f() { + enum class E { + e = T::error + }; + return (int)E(); + } + int test1 = f(); + + template + int g() { + enum class E { + e = T::error // expected-error {{has no members}} + }; + return E::e; // expected-note {{here}} + } + int test2 = g(); // expected-note {{here}} +} + +// And it cases the implicit instantiations of the definitions of: + +// - unscoped member enumerations +namespace UnscopedEnum { + template struct UnscopedEnum1 { + enum E { + e = T::error // expected-error {{'int' cannot be used prior to '::'}} + }; + }; + UnscopedEnum1 ue1; // expected-note {{here}} + + template struct UnscopedEnum2 { + enum E : T { // expected-error {{non-integral type 'void *' is an invalid underlying type}} + e = 0 + }; + }; + UnscopedEnum2 ue2; // expected-note {{here}} + + template struct UnscopedEnum3 { + enum E : T { + e = 4 + }; + int arr[E::e]; + }; + UnscopedEnum3 ue3; // ok + + template + int f() { + enum E { + e = T::error // expected-error {{has no members}} + }; + return (int)E(); + } + int test1 = f(); // expected-note {{here}} + + template + int g() { + enum E { + e = T::error // expected-error {{has no members}} + }; + return E::e; + } + int test2 = g(); // expected-note {{here}} +} + +// FIXME: +//- - member anonymous unions diff --git a/test/PCH/cxx11-enum-template.cpp b/test/PCH/cxx11-enum-template.cpp new file mode 100644 index 0000000000..70b0ff9ecb --- /dev/null +++ b/test/PCH/cxx11-enum-template.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -pedantic-errors -std=c++11 -emit-pch %s -o %t +// RUN: %clang_cc1 -pedantic-errors -std=c++11 -include-pch %t -verify %s + +#ifndef HEADER_INCLUDED + +#define HEADER_INCLUDED + +template struct S { + enum class E { + e = T() // expected-error {{conversion from 'double' to 'int'}} + }; +}; + +S a; +S::E b; +S::E c; +template struct S; + +#else + +int k1 = (int)S::E::e; +int k2 = (int)decltype(b)::e; +int k3 = (int)decltype(c)::e; // expected-note {{here}} +int k4 = (int)S::E::e; + +#endif diff --git a/www/cxx_status.html b/www/cxx_status.html index aacd9e424f..28da5f096b 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -156,7 +156,8 @@ with clang; other versions have not been tested.

Forward declarations for enums - N2764 + N2764 +
DR1206 No -- cgit v1.2.3