diff options
-rw-r--r-- | include/clang/AST/DeclTemplate.h | 4 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.def | 4 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 21 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 135 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 125 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiate.cpp | 121 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 18 | ||||
-rw-r--r-- | test/SemaTemplate/class-template-id-2.cpp | 7 | ||||
-rw-r--r-- | test/SemaTemplate/class-template-spec.cpp | 6 | ||||
-rw-r--r-- | test/SemaTemplate/instantiation-default-2.cpp | 18 |
12 files changed, 371 insertions, 102 deletions
diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 92c2d6b8f6..d93bbf5562 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -590,6 +590,10 @@ public: return template_arg_begin() + NumTemplateArgs; } + const TemplateArgument *getTemplateArgs() const { + return template_arg_begin(); + } + unsigned getNumTemplateArgs() const { return NumTemplateArgs; } /// \brief Determine the kind of specialization that this diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 781daad31a..042b155479 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -627,6 +627,10 @@ DIAG(err_template_spec_redecl_out_of_scope, ERROR, DIAG(err_template_spec_redecl_global_scope, ERROR, "class template specialization of %0 must occur in at global scope") +// C++ Template Instantiation +DIAG(err_template_implicit_instantiate_undefined, ERROR, + "implicit instantiation of undefined template %0") + DIAG(err_unexpected_typedef, ERROR, "unexpected type name %0: expected expression") DIAG(err_unexpected_namespace, ERROR, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index b1394fe788..45a3372ada 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -52,6 +52,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (this->Bases) delete [] this->Bases; + // FIXME: allocate using the ASTContext this->Bases = new CXXBaseSpecifier[NumBases]; this->NumBases = NumBases; for (unsigned i = 0; i < NumBases; ++i) diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index b9cce4b215..77f2a3d8b6 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1501,11 +1501,19 @@ public: // /// ActOnBaseSpecifier - Parsed a base specifier + CXXBaseSpecifier *CheckBaseSpecifier(CXXRecordDecl *Class, + SourceRange SpecifierRange, + bool Virtual, AccessSpecifier Access, + QualType BaseType, + SourceLocation BaseLoc); virtual BaseResult ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, bool Virtual, AccessSpecifier Access, - TypeTy *basetype, SourceLocation BaseLoc); + TypeTy *basetype, SourceLocation + BaseLoc); + bool AttachBaseSpecifiers(CXXRecordDecl *Class, CXXBaseSpecifier **Bases, + unsigned NumBases); virtual void ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, unsigned NumBases); @@ -1542,6 +1550,7 @@ public: SourceLocation DefaultLoc, TypeTy *Default); + QualType CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc); virtual DeclTy *ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, unsigned Depth, unsigned Position); @@ -1615,7 +1624,8 @@ public: bool CheckTemplateArgumentAddressOfObjectOrFunction(Expr *Arg, NamedDecl *&Entity); bool CheckTemplateArgumentPointerToMember(Expr *Arg, NamedDecl *&Member); - bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Expr *&Arg, + bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param, + QualType InstantiatedParamType, Expr *&Arg, llvm::SmallVectorImpl<TemplateArgument> *Converted = 0); bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, DeclRefExpr *Arg); bool TemplateParameterListsAreEqual(TemplateParameterList *New, @@ -1634,6 +1644,13 @@ public: QualType InstantiateType(QualType T, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs, SourceLocation Loc, DeclarationName Entity); + bool + InstantiateBaseSpecifiers(ClassTemplateSpecializationDecl *ClassTemplateSpec, + ClassTemplateDecl *ClassTemplate); + bool + InstantiateClassTemplateSpecialization( + ClassTemplateSpecializationDecl *ClassTemplateSpec, + bool ExplicitInstantiation); // Objective-C declarations. virtual DeclTy *ActOnStartClassInterface(SourceLocation AtInterfaceLoc, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 33e3921688..8d32578c98 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2174,13 +2174,16 @@ void Sema::ActOnUninitializedDecl(DeclTy *dcl) { if (Var->getStorageClass() != VarDecl::Extern && Var->getStorageClass() != VarDecl::PrivateExtern && InitType->isRecordType()) { - const CXXConstructorDecl *Constructor - = PerformInitializationByConstructor(InitType, 0, 0, - Var->getLocation(), + const CXXConstructorDecl *Constructor = 0; + if (!DiagnoseIncompleteType(Var->getLocation(), InitType, + diag::err_invalid_incomplete_type_use)) + Constructor + = PerformInitializationByConstructor(InitType, 0, 0, + Var->getLocation(), SourceRange(Var->getLocation(), Var->getLocation()), - Var->getDeclName(), - IK_Default); + Var->getDeclName(), + IK_Default); if (!Constructor) Var->setInvalidDecl(); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 1c98529fa8..3e8ee5ed1f 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -311,39 +311,48 @@ bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *, return false; } -/// ActOnBaseSpecifier - Parsed a base specifier. A base specifier is -/// one entry in the base class list of a class specifier, for -/// example: -/// class foo : public bar, virtual private baz { -/// 'public bar' and 'virtual private baz' are each base-specifiers. -Sema::BaseResult -Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, +/// \brief Check the validity of a C++ base class specifier. +/// +/// \returns a new CXXBaseSpecifier if well-formed, emits diagnostics +/// and returns NULL otherwise. +CXXBaseSpecifier * +Sema::CheckBaseSpecifier(CXXRecordDecl *Class, + SourceRange SpecifierRange, bool Virtual, AccessSpecifier Access, - TypeTy *basetype, SourceLocation BaseLoc) { - CXXRecordDecl *Decl = (CXXRecordDecl*)classdecl; - QualType BaseType = QualType::getFromOpaquePtr(basetype); + QualType BaseType, + SourceLocation BaseLoc) { + // C++ [class.union]p1: + // A union shall not have base classes. + if (Class->isUnion()) { + Diag(Class->getLocation(), diag::err_base_clause_on_union) + << SpecifierRange; + return 0; + } + + if (BaseType->isDependentType()) + return new CXXBaseSpecifier(SpecifierRange, Virtual, + Class->getTagKind() == RecordDecl::TK_class, + Access, BaseType); // Base specifiers must be record types. - if (!BaseType->isRecordType()) - return Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange; + if (!BaseType->isRecordType()) { + Diag(BaseLoc, diag::err_base_must_be_class) << SpecifierRange; + return 0; + } // C++ [class.union]p1: // A union shall not be used as a base class. - if (BaseType->isUnionType()) - return Diag(BaseLoc, diag::err_union_as_base_class) << SpecifierRange; - - // C++ [class.union]p1: - // A union shall not have base classes. - if (Decl->isUnion()) - return Diag(Decl->getLocation(), diag::err_base_clause_on_union) - << SpecifierRange; + if (BaseType->isUnionType()) { + Diag(BaseLoc, diag::err_union_as_base_class) << SpecifierRange; + return 0; + } // C++ [class.derived]p2: // The class-name in a base-specifier shall not be an incompletely // defined class. if (DiagnoseIncompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class, SpecifierRange)) - return true; + return 0; // If the base class is polymorphic, the new one is, too. RecordDecl *BaseDecl = BaseType->getAsRecordType()->getDecl(); @@ -351,25 +360,45 @@ Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, BaseDecl = BaseDecl->getDefinition(Context); assert(BaseDecl && "Base type is not incomplete, but has no definition"); if (cast<CXXRecordDecl>(BaseDecl)->isPolymorphic()) - Decl->setPolymorphic(true); + Class->setPolymorphic(true); // C++ [dcl.init.aggr]p1: // An aggregate is [...] a class with [...] no base classes [...]. - Decl->setAggregate(false); - Decl->setPOD(false); + Class->setAggregate(false); + Class->setPOD(false); // Create the base specifier. + // FIXME: Allocate via ASTContext? return new CXXBaseSpecifier(SpecifierRange, Virtual, - BaseType->isClassType(), Access, BaseType); + Class->getTagKind() == RecordDecl::TK_class, + Access, BaseType); } -/// ActOnBaseSpecifiers - Attach the given base specifiers to the -/// class, after checking whether there are any duplicate base -/// classes. -void Sema::ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, - unsigned NumBases) { - if (NumBases == 0) - return; +/// ActOnBaseSpecifier - Parsed a base specifier. A base specifier is +/// one entry in the base class list of a class specifier, for +/// example: +/// class foo : public bar, virtual private baz { +/// 'public bar' and 'virtual private baz' are each base-specifiers. +Sema::BaseResult +Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, + bool Virtual, AccessSpecifier Access, + TypeTy *basetype, SourceLocation BaseLoc) { + CXXRecordDecl *Class = (CXXRecordDecl*)classdecl; + QualType BaseType = QualType::getFromOpaquePtr(basetype); + if (CXXBaseSpecifier *BaseSpec = CheckBaseSpecifier(Class, SpecifierRange, + Virtual, Access, + BaseType, BaseLoc)) + return BaseSpec; + + return true; +} + +/// \brief Performs the actual work of attaching the given base class +/// specifiers to a C++ class. +bool Sema::AttachBaseSpecifiers(CXXRecordDecl *Class, CXXBaseSpecifier **Bases, + unsigned NumBases) { + if (NumBases == 0) + return false; // Used to keep track of which base types we have already seen, so // that we can properly diagnose redundant direct base types. Note @@ -378,40 +407,56 @@ void Sema::ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, std::map<QualType, CXXBaseSpecifier*, QualTypeOrdering> KnownBaseTypes; // Copy non-redundant base specifiers into permanent storage. - CXXBaseSpecifier **BaseSpecs = (CXXBaseSpecifier **)Bases; unsigned NumGoodBases = 0; + bool Invalid = false; for (unsigned idx = 0; idx < NumBases; ++idx) { QualType NewBaseType - = Context.getCanonicalType(BaseSpecs[idx]->getType()); + = Context.getCanonicalType(Bases[idx]->getType()); NewBaseType = NewBaseType.getUnqualifiedType(); if (KnownBaseTypes[NewBaseType]) { // C++ [class.mi]p3: // A class shall not be specified as a direct base class of a // derived class more than once. - Diag(BaseSpecs[idx]->getSourceRange().getBegin(), + Diag(Bases[idx]->getSourceRange().getBegin(), diag::err_duplicate_base_class) << KnownBaseTypes[NewBaseType]->getType() - << BaseSpecs[idx]->getSourceRange(); + << Bases[idx]->getSourceRange(); // Delete the duplicate base class specifier; we're going to // overwrite its pointer later. - delete BaseSpecs[idx]; + delete Bases[idx]; + + Invalid = true; } else { // Okay, add this new base class. - KnownBaseTypes[NewBaseType] = BaseSpecs[idx]; - BaseSpecs[NumGoodBases++] = BaseSpecs[idx]; + KnownBaseTypes[NewBaseType] = Bases[idx]; + Bases[NumGoodBases++] = Bases[idx]; } } // Attach the remaining base class specifiers to the derived class. - CXXRecordDecl *Decl = (CXXRecordDecl*)ClassDecl; - Decl->setBases(BaseSpecs, NumGoodBases); + Class->setBases(Bases, NumGoodBases); // Delete the remaining (good) base class specifiers, since their // data has been copied into the CXXRecordDecl. for (unsigned idx = 0; idx < NumGoodBases; ++idx) - delete BaseSpecs[idx]; + delete Bases[idx]; + + return Invalid; +} + +/// ActOnBaseSpecifiers - Attach the given base specifiers to the +/// class, after checking whether there are any duplicate base +/// classes. +void Sema::ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, + unsigned NumBases) { + if (!ClassDecl || !Bases || !NumBases) + return; + + AdjustDeclIfTemplate(ClassDecl); + AttachBaseSpecifiers(cast<CXXRecordDecl>((Decl*)ClassDecl), + (CXXBaseSpecifier**)(Bases), NumBases); } //===----------------------------------------------------------------------===// @@ -760,11 +805,13 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, DeclTy *TagDecl, SourceLocation LBrac, SourceLocation RBrac) { - AdjustDeclIfTemplate(TagDecl); + TemplateDecl *Template = AdjustDeclIfTemplate(TagDecl); ActOnFields(S, RLoc, TagDecl, (DeclTy**)FieldCollector->getCurFields(), FieldCollector->getCurNumFields(), LBrac, RBrac, 0); - AddImplicitlyDeclaredMembersToClass(cast<CXXRecordDecl>((Decl*)TagDecl)); + + if (!Template) + AddImplicitlyDeclaredMembersToClass(cast<CXXRecordDecl>((Decl*)TagDecl)); } /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 54f1d87cc8..ed50722d2f 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -85,7 +85,7 @@ bool Sema::DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl) { return true; } -/// AdjustDeclForTemplates - If the given decl happens to be a template, reset +/// AdjustDeclIfTemplate - If the given decl happens to be a template, reset /// the parameter D to reference the templated declaration and return a pointer /// to the template declaration. Otherwise, do nothing to D and return null. TemplateDecl *Sema::AdjustDeclIfTemplate(DeclTy *&D) @@ -164,27 +164,13 @@ void Sema::ActOnTypeParameterDefault(DeclTy *TypeParam, Parm->setDefaultArgument(Default, DefaultLoc, false); } -/// ActOnNonTypeTemplateParameter - Called when a C++ non-type -/// template parameter (e.g., "int Size" in "template<int Size> -/// class Array") has been parsed. S is the current scope and D is -/// the parsed declarator. -Sema::DeclTy *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, - unsigned Depth, - unsigned Position) { - QualType T = GetTypeForDeclarator(D, S); - - assert(S->isTemplateParamScope() && - "Non-type template parameter not in template parameter scope!"); - bool Invalid = false; - - IdentifierInfo *ParamName = D.getIdentifier(); - if (ParamName) { - NamedDecl *PrevDecl = LookupName(S, ParamName, LookupTagName); - if (PrevDecl && PrevDecl->isTemplateParameter()) - Invalid = Invalid || DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), - PrevDecl); - } - +/// \brief Check that the type of a non-type template parameter is +/// well-formed. +/// +/// \returns the (possibly-promoted) parameter type if valid; +/// otherwise, produces a diagnostic and returns a NULL type. +QualType +Sema::CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc) { // C++ [temp.param]p4: // // A non-type template-parameter shall have one of the following @@ -202,9 +188,8 @@ Sema::DeclTy *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, T->isMemberPointerType() || // If T is a dependent type, we can't do the check now, so we // assume that it is well-formed. - T->isDependentType()) { - // Okay: The template parameter is well-formed. - } + T->isDependentType()) + return T; // C++ [temp.param]p8: // // A non-type template-parameter of type "array of T" or @@ -212,16 +197,42 @@ Sema::DeclTy *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, // T" or "pointer to function returning T", respectively. else if (T->isArrayType()) // FIXME: Keep the type prior to promotion? - T = Context.getArrayDecayedType(T); + return Context.getArrayDecayedType(T); else if (T->isFunctionType()) // FIXME: Keep the type prior to promotion? - T = Context.getPointerType(T); - else { - Diag(D.getIdentifierLoc(), diag::err_template_nontype_parm_bad_type) - << T; - return 0; + return Context.getPointerType(T); + + Diag(Loc, diag::err_template_nontype_parm_bad_type) + << T; + + return QualType(); +} + +/// ActOnNonTypeTemplateParameter - Called when a C++ non-type +/// template parameter (e.g., "int Size" in "template<int Size> +/// class Array") has been parsed. S is the current scope and D is +/// the parsed declarator. +Sema::DeclTy *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, + unsigned Depth, + unsigned Position) { + QualType T = GetTypeForDeclarator(D, S); + + assert(S->isTemplateParamScope() && + "Non-type template parameter not in template parameter scope!"); + bool Invalid = false; + + IdentifierInfo *ParamName = D.getIdentifier(); + if (ParamName) { + NamedDecl *PrevDecl = LookupName(S, ParamName, LookupTagName); + if (PrevDecl && PrevDecl->isTemplateParameter()) + Invalid = Invalid || DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), + PrevDecl); } + T = CheckNonTypeTemplateParameterType(T, D.getIdentifierLoc()); + if (T.isNull()) + T = Context.IntTy; // Recover with an 'int' type. + NonTypeTemplateParmDecl *Param = NonTypeTemplateParmDecl::Create(Context, CurContext, D.getIdentifierLoc(), Depth, Position, ParamName, T); @@ -250,7 +261,7 @@ void Sema::ActOnNonTypeTemplateParameterDefault(DeclTy *TemplateParamD, // FIXME: Implement this check! Needs a recursive walk over the types. // Check the well-formedness of the default template argument. - if (CheckTemplateArgument(TemplateParm, Default)) { + if (CheckTemplateArgument(TemplateParm, TemplateParm->getType(), Default)) { TemplateParm->setInvalidDecl(); return; } @@ -774,6 +785,7 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, if (!NTTP->hasDefaultArgument()) break; + // FIXME: Instantiate default argument ArgExpr = NTTP->getDefaultArgument(); ArgLoc = NTTP->getDefaultArgumentLoc(); } else { @@ -783,6 +795,7 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, if (!TempParm->hasDefaultArgument()) break; + // FIXME: Instantiate default argument ArgExpr = TempParm->getDefaultArgument(); ArgLoc = TempParm->getDefaultArgumentLoc(); } @@ -822,8 +835,30 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, } else if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*Param)) { // Check non-type template parameters. + + // Instantiate the type of the non-type template parameter with + // the template arguments we've seen thus far. + QualType NTTPType = NTTP->getType(); + if (NTTPType->isDependentType()) { + // Instantiate the type of the non-type template parameter. + NTTPType = InstantiateType(NTTPType, + &Converted[0], Converted.size(), + NTTP->getLocation(), + NTTP->getDeclName()); + // If that worked, check the non-type template parameter type + // for validity. + if (!NTTPType.isNull()) + NTTPType = CheckNonTypeTemplateParameterType(NTTPType, + NTTP->getLocation()); + + if (NTTPType.isNull()) { + Invalid = true; + break; + } + } + if (ArgExpr) { - if (CheckTemplateArgument(NTTP, ArgExpr, &Converted)) + if (CheckTemplateArgument(NTTP, NTTPType, ArgExpr, &Converted)) Invalid = true; continue; } @@ -1062,18 +1097,20 @@ Sema::CheckTemplateArgumentPointerToMember(Expr *Arg, NamedDecl *&Member) { /// \brief Check a template argument against its corresponding /// non-type template parameter. /// -/// This routine implements the semantics of C++ [temp.arg.nontype]. -/// It returns true if an error occurred, and false otherwise. +/// This routine implements the semantics of C++ [temp.arg.nontype]. +/// It returns true if an error occurred, and false otherwise. \p +/// InstantiatedParamType is the type of the non-type template +/// parameter after it has been instantiated. /// /// If Converted is non-NULL and no errors occur, the value /// of this argument will be added to the end of the Converted vector. bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, - Expr *&Arg, + QualType InstantiatedParamType, Expr *&Arg, llvm::SmallVectorImpl<TemplateArgument> *Converted) { // If either the parameter has a dependent type or the argument is // type-dependent, there's nothing we can check now. // FIXME: Add template argument to Converted! - if (Param->getType()->isDependentType() || Arg->isTypeDependent()) + if (InstantiatedParamType->isDependentType() || Arg->isTypeDependent()) return false; // C++ [temp.arg.nontype]p5: @@ -1086,7 +1123,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // -- for a non-type template-parameter of integral or // enumeration type, integral promotions (4.5) and integral // conversions (4.7) are applied. - QualType ParamType = Param->getType(); + QualType ParamType = InstantiatedParamType; QualType ArgType = Arg->getType(); if (ParamType->isIntegralType() || ParamType->isEnumeralType()) { // C++ [temp.arg.nontype]p1: @@ -1130,7 +1167,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // We can't perform this conversion. Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_not_convertible) - << Arg->getType() << Param->getType() << Arg->getSourceRange(); + << Arg->getType() << InstantiatedParamType << Arg->getSourceRange(); Diag(Param->getLocation(), diag::note_template_param_here); return true; } @@ -1202,7 +1239,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // We can't perform this conversion. Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_not_convertible) - << Arg->getType() << Param->getType() << Arg->getSourceRange(); + << Arg->getType() << InstantiatedParamType << Arg->getSourceRange(); Diag(Param->getLocation(), diag::note_template_param_here); return true; } @@ -1248,7 +1285,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // We can't perform this conversion. Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_not_convertible) - << Arg->getType() << Param->getType() << Arg->getSourceRange(); + << Arg->getType() << InstantiatedParamType << Arg->getSourceRange(); Diag(Param->getLocation(), diag::note_template_param_here); return true; } @@ -1276,7 +1313,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, if (!Context.hasSameUnqualifiedType(ParamRefType->getPointeeType(), ArgType)) { Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_no_ref_bind) - << Param->getType() << Arg->getType() + << InstantiatedParamType << Arg->getType() << Arg->getSourceRange(); Diag(Param->getLocation(), diag::note_template_param_here); return true; @@ -1289,7 +1326,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, if ((ParamQuals | ArgQuals) != ParamQuals) { Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_ref_bind_ignores_quals) - << Param->getType() << Arg->getType() + << InstantiatedParamType << Arg->getType() << Arg->getSourceRange(); Diag(Param->getLocation(), diag::note_template_param_here); return true; @@ -1317,7 +1354,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // We can't perform this conversion. Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_not_convertible) - << Arg->getType() << Param->getType() << Arg->getSourceRange(); + << Arg->getType() << InstantiatedParamType << Arg->getSourceRange(); Diag(Param->getLocation(), diag::note_template_param_here); return true; } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 6b43463777..faeebc0680 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -399,3 +399,124 @@ QualType Sema::InstantiateType(QualType T, Loc, Entity); return Instantiator(T); } + +/// \brief Instantiate the base class specifiers of the given class +/// template specialization. +/// +/// Produces a diagnostic and returns true on error, returns false and +/// attaches the instantiated base classes to the class template +/// specialization if successful. +bool +Sema::InstantiateBaseSpecifiers( + ClassTemplateSpecializationDecl *ClassTemplateSpec, + ClassTemplateDecl *ClassTemplate) { + bool Invalid = false; + llvm::SmallVector<CXXBaseSpecifier*, 8> InstantiatedBases; + for (ClassTemplateSpecializationDecl::base_class_iterator + Base = ClassTemplate->getTemplatedDecl()->bases_begin(), + BaseEnd = ClassTemplate->getTemplatedDecl()->bases_end(); + Base != BaseEnd && !Invalid; ++Base) { + if (!Base->getType()->isDependentType()) { + // FIXME: Allocate via ASTContext + InstantiatedBases.push_back(new CXXBaseSpecifier(*Base)); + continue; + } + + QualType BaseType = InstantiateType(Base->getType(), + ClassTemplateSpec->getTemplateArgs(), + ClassTemplateSpec->getNumTemplateArgs(), + Base->getSourceRange().getBegin(), + DeclarationName()); + if (BaseType.isNull()) { + Invalid = true; + continue; + } + + if (CXXBaseSpecifier *InstantiatedBase + = CheckBaseSpecifier(ClassTemplateSpec, + Base->getSourceRange(), + Base->isVirtual(), + Base->getAccessSpecifierAsWritten(), + BaseType, + /*FIXME: Not totally accurate */ + Base->getSourceRange().getBegin())) + InstantiatedBases.push_back(InstantiatedBase); + else + Invalid = true; + } + + if (AttachBaseSpecifiers(ClassTemplateSpec, &InstantiatedBases[0], + InstantiatedBases.size())) + Invalid = true; + + return Invalid; +} + +bool +Sema::InstantiateClassTemplateSpecialization( + ClassTemplateSpecializationDecl *ClassTemplateSpec, + bool ExplicitInstantiation) { + // Perform the actual instantiation on the canonical declaration. + ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>( + Context.getCanonicalDecl(ClassTemplateSpec)); + + // We can only instantiate something that hasn't already been + // instantiated or specialized. Fail without any diagnostics: our + // caller will provide an error message. + if (ClassTemplateSpec->getSpecializationKind() != TSK_Undeclared) + return true; + + // FIXME: Push this class template instantiation onto the + // instantiation stack, checking for recursion that exceeds a + // certain depth. + + // FIXME: Perform class template partial specialization to select + // the best template. + ClassTemplateDecl *Template = ClassTemplateSpec->getSpecializedTemplate(); + + if (!Template->getTemplatedDecl()->getDefinition(Context)) { + Diag(ClassTemplateSpec->getLocation(), + diag::err_template_implicit_instantiate_undefined) + << Context.getTypeDeclType(ClassTemplateSpec); + Diag(Template->getTemplatedDecl()->getLocation(), + diag::note_template_decl_here); + return true; + } + + // Note that this is an instantiation. + ClassTemplateSpec->setSpecializationKind( + ExplicitInstantiation? TSK_ExplicitInstantiation + : TSK_ImplicitInstantiation); + + + bool Invalid = false; + + // Enter the scope of this instantiation. We don't use + // PushDeclContext because we don't have a scope. + DeclContext *PreviousContext = CurContext; + CurContext = ClassTemplateSpec; + + // Start the definition of this instantiation. + ClassTemplateSpec->startDefinition(); + + // FIXME: Create the injected-class-name for the + // instantiation. Should this be a typedef or something like it? + + // Instantiate the base class specifiers. + if (InstantiateBaseSpecifiers(ClassTemplateSpec, Template)) + Invalid = true; + + // FIXME: Instantiate all of the members. + + // Add any implicitly-declared members that we might need. + AddImplicitlyDeclaredMembersToClass(ClassTemplateSpec); + + // Finish the definition of this instantiation. + // FIXME: ActOnFields does more checking, which we'll eventually need. + ClassTemplateSpec->completeDefinition(Context); + + // Exit the scope of this instantiation. + CurContext = PreviousContext; + + return Invalid; +} diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 69cd884300..96d17be931 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -14,6 +14,7 @@ #include "Sema.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/Parse/DeclSpec.h" using namespace clang; @@ -473,7 +474,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, /// This routine checks the function type according to C++ rules and /// under the assumption that the result type and parameter types have /// just been instantiated from a template. It therefore duplicates -/// some of the behavior of GetTypeForDeclaration, but in a much +/// some of the behavior of GetTypeForDeclarator, but in a much /// simpler form that is only suitable for this narrow use case. /// /// \param T The return type of the function. @@ -1033,6 +1034,21 @@ bool Sema::DiagnoseIncompleteType(SourceLocation Loc, QualType T, unsigned diag, if (!T->isIncompleteType()) return false; + // If we have a class template specialization, try to instantiate + // it. + if (const RecordType *Record = T->getAsRecordType()) + if (ClassTemplateSpecializationDecl *ClassTemplateSpec + = dyn_cast<ClassTemplateSpecializationDecl>(Record->getDecl())) + if (ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared) { + // Update the class template specialization's location to + // refer to the point of instantiation. + if (Loc.isValid()) + ClassTemplateSpec->setLocation(Loc); + return InstantiateClassTemplateSpecialization(ClassTemplateSpec, + /*ExplicitInstantiation=*/false); + } + + if (PrintType.isNull()) PrintType = T; diff --git a/test/SemaTemplate/class-template-id-2.cpp b/test/SemaTemplate/class-template-id-2.cpp index dee4735de9..3014208b6e 100644 --- a/test/SemaTemplate/class-template-id-2.cpp +++ b/test/SemaTemplate/class-template-id-2.cpp @@ -1,16 +1,17 @@ // RUN: clang -fsyntax-only -verify %s namespace N { - template<typename T> class A; + template<typename T> class A { }; template<> class A<int> { }; + template<> class A<float>; // expected-note{{forward declaration of 'class A'}} + class B : public A<int> { }; } class C1 : public N::A<int> { }; -class C2 : public N::A<float> { }; // expected-error{{base class has incomplete type}} \ - // FIXME: expected-note{{forward declaration of 'class A'}} +class C2 : public N::A<float> { }; // expected-error{{base class has incomplete type}} struct D1 { operator N::A<int>(); diff --git a/test/SemaTemplate/class-template-spec.cpp b/test/SemaTemplate/class-template-spec.cpp index e4900838f1..023ba381ec 100644 --- a/test/SemaTemplate/class-template-spec.cpp +++ b/test/SemaTemplate/class-template-spec.cpp @@ -1,5 +1,5 @@ // RUN: clang -fsyntax-only -verify %s -template<typename T, typename U = int> struct A; // expected-note{{template is declared here}} +template<typename T, typename U = int> struct A; // expected-note 2{{template is declared here}} template<> struct A<double, double>; // expected-note{{forward declaration}} @@ -16,10 +16,10 @@ int test_specs(A<float, float> *a1, A<float, int> *a2) { } int test_incomplete_specs(A<double, double> *a1, - A<double> *a2) // FIXME: expected-note{{forward declaration}} + |