forked from OSchip/llvm-project
Implement AST import support for class template specializations.
llvm-svn: 120523
This commit is contained in:
parent
7c646b924b
commit
e2e50d331c
|
@ -125,6 +125,10 @@ namespace clang {
|
|||
/// context, or NULL if an error occurred.
|
||||
NestedNameSpecifier *Import(NestedNameSpecifier *FromNNS);
|
||||
|
||||
/// \brief Import the goven template name from the "from" context into the
|
||||
/// "to" context.
|
||||
TemplateName Import(TemplateName From);
|
||||
|
||||
/// \brief Import the given source location from the "from" context into
|
||||
/// the "to" context.
|
||||
///
|
||||
|
@ -150,7 +154,7 @@ namespace clang {
|
|||
/// into the "to" context.
|
||||
///
|
||||
/// \returns the equivalent identifier in the "to" context.
|
||||
IdentifierInfo *Import(IdentifierInfo *FromId);
|
||||
IdentifierInfo *Import(const IdentifierInfo *FromId);
|
||||
|
||||
/// \brief Import the given Objective-C selector from the "from"
|
||||
/// context into the "to" context.
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace {
|
|||
QualType VisitEnumType(EnumType *T);
|
||||
// FIXME: TemplateTypeParmType
|
||||
// FIXME: SubstTemplateTypeParmType
|
||||
// FIXME: TemplateSpecializationType
|
||||
QualType VisitTemplateSpecializationType(TemplateSpecializationType *T);
|
||||
QualType VisitElaboratedType(ElaboratedType *T);
|
||||
// FIXME: DependentNameType
|
||||
// FIXME: DependentTemplateSpecializationType
|
||||
|
@ -84,8 +84,13 @@ namespace {
|
|||
void ImportDeclarationNameLoc(const DeclarationNameInfo &From,
|
||||
DeclarationNameInfo& To);
|
||||
void ImportDeclContext(DeclContext *FromDC);
|
||||
bool ImportDefinition(RecordDecl *From, RecordDecl *To);
|
||||
TemplateParameterList *ImportTemplateParameterList(
|
||||
TemplateParameterList *Params);
|
||||
TemplateArgument ImportTemplateArgument(const TemplateArgument &From);
|
||||
bool ImportTemplateArguments(const TemplateArgument *FromArgs,
|
||||
unsigned NumFromArgs,
|
||||
llvm::SmallVectorImpl<TemplateArgument> &ToArgs);
|
||||
bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord);
|
||||
bool IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToRecord);
|
||||
bool IsStructuralMatch(ClassTemplateDecl *From, ClassTemplateDecl *To);
|
||||
|
@ -117,6 +122,8 @@ namespace {
|
|||
Decl *VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D);
|
||||
Decl *VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D);
|
||||
Decl *VisitClassTemplateDecl(ClassTemplateDecl *D);
|
||||
Decl *VisitClassTemplateSpecializationDecl(
|
||||
ClassTemplateSpecializationDecl *D);
|
||||
|
||||
// Importing statements
|
||||
Stmt *VisitStmt(Stmt *S);
|
||||
|
@ -267,7 +274,49 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
|||
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
||||
const TemplateArgument &Arg1,
|
||||
const TemplateArgument &Arg2) {
|
||||
// FIXME: Implement!
|
||||
if (Arg1.getKind() != Arg2.getKind())
|
||||
return false;
|
||||
|
||||
switch (Arg1.getKind()) {
|
||||
case TemplateArgument::Null:
|
||||
return true;
|
||||
|
||||
case TemplateArgument::Type:
|
||||
return Context.IsStructurallyEquivalent(Arg1.getAsType(), Arg2.getAsType());
|
||||
|
||||
case TemplateArgument::Integral:
|
||||
if (!Context.IsStructurallyEquivalent(Arg1.getIntegralType(),
|
||||
Arg2.getIntegralType()))
|
||||
return false;
|
||||
|
||||
return IsSameValue(*Arg1.getAsIntegral(), *Arg2.getAsIntegral());
|
||||
|
||||
case TemplateArgument::Declaration:
|
||||
return Context.IsStructurallyEquivalent(Arg1.getAsDecl(), Arg2.getAsDecl());
|
||||
|
||||
case TemplateArgument::Template:
|
||||
return IsStructurallyEquivalent(Context,
|
||||
Arg1.getAsTemplate(),
|
||||
Arg2.getAsTemplate());
|
||||
|
||||
case TemplateArgument::Expression:
|
||||
return IsStructurallyEquivalent(Context,
|
||||
Arg1.getAsExpr(), Arg2.getAsExpr());
|
||||
|
||||
case TemplateArgument::Pack:
|
||||
if (Arg1.pack_size() != Arg2.pack_size())
|
||||
return false;
|
||||
|
||||
for (unsigned I = 0, N = Arg1.pack_size(); I != N; ++I)
|
||||
if (!IsStructurallyEquivalent(Context,
|
||||
Arg1.pack_begin()[I],
|
||||
Arg2.pack_begin()[I]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm_unreachable("Invalid template argument kind");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -702,6 +751,33 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
|||
return false;
|
||||
}
|
||||
|
||||
// If both declarations are class template specializations, we know
|
||||
// the ODR applies, so check the template and template arguments.
|
||||
ClassTemplateSpecializationDecl *Spec1
|
||||
= dyn_cast<ClassTemplateSpecializationDecl>(D1);
|
||||
ClassTemplateSpecializationDecl *Spec2
|
||||
= dyn_cast<ClassTemplateSpecializationDecl>(D2);
|
||||
if (Spec1 && Spec2) {
|
||||
// Check that the specialized templates are the same.
|
||||
if (!IsStructurallyEquivalent(Context, Spec1->getSpecializedTemplate(),
|
||||
Spec2->getSpecializedTemplate()))
|
||||
return false;
|
||||
|
||||
// Check that the template arguments are the same.
|
||||
if (Spec1->getTemplateArgs().size() != Spec2->getTemplateArgs().size())
|
||||
return false;
|
||||
|
||||
for (unsigned I = 0, N = Spec1->getTemplateArgs().size(); I != N; ++I)
|
||||
if (!IsStructurallyEquivalent(Context,
|
||||
Spec1->getTemplateArgs().get(I),
|
||||
Spec2->getTemplateArgs().get(I)))
|
||||
return false;
|
||||
}
|
||||
// If one is a class template specialization and the other is not, these
|
||||
// structures are diferent.
|
||||
else if (Spec1 || Spec2)
|
||||
return false;
|
||||
|
||||
// Compare the definitions of these two records. If either or both are
|
||||
// incomplete, we assume that they are equivalent.
|
||||
D1 = D1->getDefinition();
|
||||
|
@ -1450,6 +1526,30 @@ QualType ASTNodeImporter::VisitEnumType(EnumType *T) {
|
|||
return Importer.getToContext().getTagDeclType(ToDecl);
|
||||
}
|
||||
|
||||
QualType ASTNodeImporter::VisitTemplateSpecializationType(
|
||||
TemplateSpecializationType *T) {
|
||||
TemplateName ToTemplate = Importer.Import(T->getTemplateName());
|
||||
if (ToTemplate.isNull())
|
||||
return QualType();
|
||||
|
||||
llvm::SmallVector<TemplateArgument, 2> ToTemplateArgs;
|
||||
if (ImportTemplateArguments(T->getArgs(), T->getNumArgs(), ToTemplateArgs))
|
||||
return QualType();
|
||||
|
||||
QualType ToCanonType;
|
||||
if (!QualType(T, 0).isCanonical()) {
|
||||
QualType FromCanonType
|
||||
= Importer.getFromContext().getCanonicalType(QualType(T, 0));
|
||||
ToCanonType =Importer.Import(FromCanonType);
|
||||
if (ToCanonType.isNull())
|
||||
return QualType();
|
||||
}
|
||||
return Importer.getToContext().getTemplateSpecializationType(ToTemplate,
|
||||
ToTemplateArgs.data(),
|
||||
ToTemplateArgs.size(),
|
||||
ToCanonType);
|
||||
}
|
||||
|
||||
QualType ASTNodeImporter::VisitElaboratedType(ElaboratedType *T) {
|
||||
NestedNameSpecifier *ToQualifier = 0;
|
||||
// Note: the qualifier in an ElaboratedType is optional.
|
||||
|
@ -1576,6 +1676,43 @@ void ASTNodeImporter::ImportDeclContext(DeclContext *FromDC) {
|
|||
Importer.Import(*From);
|
||||
}
|
||||
|
||||
bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To) {
|
||||
if (To->getDefinition())
|
||||
return false;
|
||||
|
||||
To->startDefinition();
|
||||
|
||||
// Add base classes.
|
||||
if (CXXRecordDecl *ToCXX = dyn_cast<CXXRecordDecl>(To)) {
|
||||
CXXRecordDecl *FromCXX = cast<CXXRecordDecl>(From);
|
||||
|
||||
llvm::SmallVector<CXXBaseSpecifier *, 4> Bases;
|
||||
for (CXXRecordDecl::base_class_iterator
|
||||
Base1 = FromCXX->bases_begin(),
|
||||
FromBaseEnd = FromCXX->bases_end();
|
||||
Base1 != FromBaseEnd;
|
||||
++Base1) {
|
||||
QualType T = Importer.Import(Base1->getType());
|
||||
if (T.isNull())
|
||||
return false;
|
||||
|
||||
Bases.push_back(
|
||||
new (Importer.getToContext())
|
||||
CXXBaseSpecifier(Importer.Import(Base1->getSourceRange()),
|
||||
Base1->isVirtual(),
|
||||
Base1->isBaseOfClass(),
|
||||
Base1->getAccessSpecifierAsWritten(),
|
||||
Importer.Import(Base1->getTypeSourceInfo())));
|
||||
}
|
||||
if (!Bases.empty())
|
||||
ToCXX->setBases(Bases.data(), Bases.size());
|
||||
}
|
||||
|
||||
ImportDeclContext(From);
|
||||
To->completeDefinition();
|
||||
return true;
|
||||
}
|
||||
|
||||
TemplateParameterList *ASTNodeImporter::ImportTemplateParameterList(
|
||||
TemplateParameterList *Params) {
|
||||
llvm::SmallVector<NamedDecl *, 4> ToParams;
|
||||
|
@ -1597,6 +1734,75 @@ TemplateParameterList *ASTNodeImporter::ImportTemplateParameterList(
|
|||
Importer.Import(Params->getRAngleLoc()));
|
||||
}
|
||||
|
||||
TemplateArgument
|
||||
ASTNodeImporter::ImportTemplateArgument(const TemplateArgument &From) {
|
||||
switch (From.getKind()) {
|
||||
case TemplateArgument::Null:
|
||||
return TemplateArgument();
|
||||
|
||||
case TemplateArgument::Type: {
|
||||
QualType ToType = Importer.Import(From.getAsType());
|
||||
if (ToType.isNull())
|
||||
return TemplateArgument();
|
||||
return TemplateArgument(ToType);
|
||||
}
|
||||
|
||||
case TemplateArgument::Integral: {
|
||||
QualType ToType = Importer.Import(From.getIntegralType());
|
||||
if (ToType.isNull())
|
||||
return TemplateArgument();
|
||||
return TemplateArgument(*From.getAsIntegral(), ToType);
|
||||
}
|
||||
|
||||
case TemplateArgument::Declaration:
|
||||
if (Decl *To = Importer.Import(From.getAsDecl()))
|
||||
return TemplateArgument(To);
|
||||
return TemplateArgument();
|
||||
|
||||
case TemplateArgument::Template: {
|
||||
TemplateName ToTemplate = Importer.Import(From.getAsTemplate());
|
||||
if (ToTemplate.isNull())
|
||||
return TemplateArgument();
|
||||
|
||||
return TemplateArgument(ToTemplate);
|
||||
}
|
||||
|
||||
case TemplateArgument::Expression:
|
||||
if (Expr *ToExpr = Importer.Import(From.getAsExpr()))
|
||||
return TemplateArgument(ToExpr);
|
||||
return TemplateArgument();
|
||||
|
||||
case TemplateArgument::Pack: {
|
||||
llvm::SmallVector<TemplateArgument, 2> ToPack;
|
||||
ToPack.reserve(From.pack_size());
|
||||
if (ImportTemplateArguments(From.pack_begin(), From.pack_size(), ToPack))
|
||||
return TemplateArgument();
|
||||
|
||||
TemplateArgument *ToArgs
|
||||
= new (Importer.getToContext()) TemplateArgument[ToPack.size()];
|
||||
std::copy(ToPack.begin(), ToPack.end(), ToArgs);
|
||||
return TemplateArgument(ToArgs, ToPack.size());
|
||||
}
|
||||
}
|
||||
|
||||
llvm_unreachable("Invalid template argument kind");
|
||||
return TemplateArgument();
|
||||
}
|
||||
|
||||
bool ASTNodeImporter::ImportTemplateArguments(const TemplateArgument *FromArgs,
|
||||
unsigned NumFromArgs,
|
||||
llvm::SmallVectorImpl<TemplateArgument> &ToArgs) {
|
||||
for (unsigned I = 0; I != NumFromArgs; ++I) {
|
||||
TemplateArgument To = ImportTemplateArgument(FromArgs[I]);
|
||||
if (To.isNull() && !FromArgs[I].isNull())
|
||||
return true;
|
||||
|
||||
ToArgs.push_back(To);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord,
|
||||
RecordDecl *ToRecord) {
|
||||
StructuralEquivalenceContext Ctx(Importer.getFromContext(),
|
||||
|
@ -1939,38 +2145,8 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
|
|||
|
||||
Importer.Imported(D, D2);
|
||||
|
||||
if (D->isDefinition()) {
|
||||
D2->startDefinition();
|
||||
|
||||
// Add base classes.
|
||||
if (CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(D2)) {
|
||||
CXXRecordDecl *D1CXX = cast<CXXRecordDecl>(D);
|
||||
|
||||
llvm::SmallVector<CXXBaseSpecifier *, 4> Bases;
|
||||
for (CXXRecordDecl::base_class_iterator
|
||||
Base1 = D1CXX->bases_begin(),
|
||||
FromBaseEnd = D1CXX->bases_end();
|
||||
Base1 != FromBaseEnd;
|
||||
++Base1) {
|
||||
QualType T = Importer.Import(Base1->getType());
|
||||
if (T.isNull())
|
||||
return 0;
|
||||
|
||||
Bases.push_back(
|
||||
new (Importer.getToContext())
|
||||
CXXBaseSpecifier(Importer.Import(Base1->getSourceRange()),
|
||||
Base1->isVirtual(),
|
||||
Base1->isBaseOfClass(),
|
||||
Base1->getAccessSpecifierAsWritten(),
|
||||
Importer.Import(Base1->getTypeSourceInfo())));
|
||||
}
|
||||
if (!Bases.empty())
|
||||
D2CXX->setBases(Bases.data(), Bases.size());
|
||||
}
|
||||
|
||||
ImportDeclContext(D);
|
||||
D2->completeDefinition();
|
||||
}
|
||||
if (D->isDefinition() && ImportDefinition(D, D2))
|
||||
return 0;
|
||||
|
||||
return D2;
|
||||
}
|
||||
|
@ -3166,6 +3342,100 @@ Decl *ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
|
|||
return D2;
|
||||
}
|
||||
|
||||
Decl *ASTNodeImporter::VisitClassTemplateSpecializationDecl(
|
||||
ClassTemplateSpecializationDecl *D) {
|
||||
// If this record has a definition in the translation unit we're coming from,
|
||||
// but this particular declaration is not that definition, import the
|
||||
// definition and map to that.
|
||||
TagDecl *Definition = D->getDefinition();
|
||||
if (Definition && Definition != D) {
|
||||
Decl *ImportedDef = Importer.Import(Definition);
|
||||
if (!ImportedDef)
|
||||
return 0;
|
||||
|
||||
return Importer.Imported(D, ImportedDef);
|
||||
}
|
||||
|
||||
ClassTemplateDecl *ClassTemplate
|
||||
= cast_or_null<ClassTemplateDecl>(Importer.Import(
|
||||
D->getSpecializedTemplate()));
|
||||
if (!ClassTemplate)
|
||||
return 0;
|
||||
|
||||
// Import the context of this declaration.
|
||||
DeclContext *DC = ClassTemplate->getDeclContext();
|
||||
if (!DC)
|
||||
return 0;
|
||||
|
||||
DeclContext *LexicalDC = DC;
|
||||
if (D->getDeclContext() != D->getLexicalDeclContext()) {
|
||||
LexicalDC = Importer.ImportContext(D->getLexicalDeclContext());
|
||||
if (!LexicalDC)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Import the location of this declaration.
|
||||
SourceLocation Loc = Importer.Import(D->getLocation());
|
||||
|
||||
// Import template arguments.
|
||||
llvm::SmallVector<TemplateArgument, 2> TemplateArgs;
|
||||
if (ImportTemplateArguments(D->getTemplateArgs().data(),
|
||||
D->getTemplateArgs().size(),
|
||||
TemplateArgs))
|
||||
return 0;
|
||||
|
||||
// Try to find an existing specialization with these template arguments.
|
||||
void *InsertPos = 0;
|
||||
ClassTemplateSpecializationDecl *D2
|
||||
= ClassTemplate->findSpecialization(TemplateArgs.data(),
|
||||
TemplateArgs.size(), InsertPos);
|
||||
if (D2) {
|
||||
// We already have a class template specialization with these template
|
||||
// arguments.
|
||||
|
||||
// FIXME: Check for specialization vs. instantiation errors.
|
||||
|
||||
if (RecordDecl *FoundDef = D2->getDefinition()) {
|
||||
if (!D->isDefinition() || IsStructuralMatch(D, FoundDef)) {
|
||||
// The record types structurally match, or the "from" translation
|
||||
// unit only had a forward declaration anyway; call it the same
|
||||
// function.
|
||||
return Importer.Imported(D, FoundDef);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create a new specialization.
|
||||
D2 = ClassTemplateSpecializationDecl::Create(Importer.getToContext(),
|
||||
D->getTagKind(), DC,
|
||||
Loc, ClassTemplate,
|
||||
TemplateArgs.data(),
|
||||
TemplateArgs.size(),
|
||||
/*PrevDecl=*/0);
|
||||
D2->setSpecializationKind(D->getSpecializationKind());
|
||||
|
||||
// Add this specialization to the class template.
|
||||
ClassTemplate->AddSpecialization(D2, InsertPos);
|
||||
|
||||
// Import the qualifier, if any.
|
||||
if (D->getQualifier()) {
|
||||
NestedNameSpecifier *NNS = Importer.Import(D->getQualifier());
|
||||
SourceRange NNSRange = Importer.Import(D->getQualifierRange());
|
||||
D2->setQualifierInfo(NNS, NNSRange);
|
||||
}
|
||||
|
||||
|
||||
// Add the specialization to this context.
|
||||
D2->setLexicalDeclContext(LexicalDC);
|
||||
LexicalDC->addDecl(D2);
|
||||
}
|
||||
Importer.Imported(D, D2);
|
||||
|
||||
if (D->isDefinition() && ImportDefinition(D, D2))
|
||||
return 0;
|
||||
|
||||
return D2;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Import Statements
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -3506,6 +3776,64 @@ NestedNameSpecifier *ASTImporter::Import(NestedNameSpecifier *FromNNS) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
TemplateName ASTImporter::Import(TemplateName From) {
|
||||
switch (From.getKind()) {
|
||||
case TemplateName::Template:
|
||||
if (TemplateDecl *ToTemplate
|
||||
= cast_or_null<TemplateDecl>(Import(From.getAsTemplateDecl())))
|
||||
return TemplateName(ToTemplate);
|
||||
|
||||
return TemplateName();
|
||||
|
||||
case TemplateName::OverloadedTemplate: {
|
||||
OverloadedTemplateStorage *FromStorage = From.getAsOverloadedTemplate();
|
||||
UnresolvedSet<2> ToTemplates;
|
||||
for (OverloadedTemplateStorage::iterator I = FromStorage->begin(),
|
||||
E = FromStorage->end();
|
||||
I != E; ++I) {
|
||||
if (NamedDecl *To = cast_or_null<NamedDecl>(Import(*I)))
|
||||
ToTemplates.addDecl(To);
|
||||
else
|
||||
return TemplateName();
|
||||
}
|
||||
return ToContext.getOverloadedTemplateName(ToTemplates.begin(),
|
||||
ToTemplates.end());
|
||||
}
|
||||
|
||||
case TemplateName::QualifiedTemplate: {
|
||||
QualifiedTemplateName *QTN = From.getAsQualifiedTemplateName();
|
||||
NestedNameSpecifier *Qualifier = Import(QTN->getQualifier());
|
||||
if (!Qualifier)
|
||||
return TemplateName();
|
||||
|
||||
if (TemplateDecl *ToTemplate
|
||||
= cast_or_null<TemplateDecl>(Import(From.getAsTemplateDecl())))
|
||||
return ToContext.getQualifiedTemplateName(Qualifier,
|
||||
QTN->hasTemplateKeyword(),
|
||||
ToTemplate);
|
||||
|
||||
return TemplateName();
|
||||
}
|
||||
|
||||
case TemplateName::DependentTemplate: {
|
||||
DependentTemplateName *DTN = From.getAsDependentTemplateName();
|
||||
NestedNameSpecifier *Qualifier = Import(DTN->getQualifier());
|
||||
if (!Qualifier)
|
||||
return TemplateName();
|
||||
|
||||
if (DTN->isIdentifier()) {
|
||||
return ToContext.getDependentTemplateName(Qualifier,
|
||||
Import(DTN->getIdentifier()));
|
||||
}
|
||||
|
||||
return ToContext.getDependentTemplateName(Qualifier, DTN->getOperator());
|
||||
}
|
||||
}
|
||||
|
||||
llvm_unreachable("Invalid template name kind");
|
||||
return TemplateName();
|
||||
}
|
||||
|
||||
SourceLocation ASTImporter::Import(SourceLocation FromLoc) {
|
||||
if (FromLoc.isInvalid())
|
||||
return SourceLocation();
|
||||
|
@ -3623,7 +3951,7 @@ DeclarationName ASTImporter::Import(DeclarationName FromName) {
|
|||
return DeclarationName();
|
||||
}
|
||||
|
||||
IdentifierInfo *ASTImporter::Import(IdentifierInfo *FromId) {
|
||||
IdentifierInfo *ASTImporter::Import(const IdentifierInfo *FromId) {
|
||||
if (!FromId)
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -19,3 +19,16 @@ struct X5;
|
|||
template<typename>
|
||||
struct X6;
|
||||
|
||||
extern X0<int> *x0i;
|
||||
extern X0<long> *x0l;
|
||||
extern X0<float> *x0r;
|
||||
|
||||
template<>
|
||||
struct X0<char> {
|
||||
int member;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct X0<wchar_t> {
|
||||
int member;
|
||||
};
|
||||
|
|
|
@ -18,3 +18,18 @@ struct X5;
|
|||
|
||||
template<template<int I> class>
|
||||
struct X6;
|
||||
|
||||
typedef int Integer;
|
||||
extern X0<Integer> *x0i;
|
||||
extern X0<float> *x0f;
|
||||
extern X0<double> *x0r;
|
||||
|
||||
template<>
|
||||
struct X0<char> {
|
||||
int member;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct X0<wchar_t> {
|
||||
float member;
|
||||
};
|
||||
|
|
|
@ -13,3 +13,12 @@
|
|||
|
||||
// CHECK: class-template1.cpp:19:10: error: template parameter has different kinds in different translation units
|
||||
// CHECK: class-template2.cpp:19:10: note: template parameter declared here
|
||||
|
||||
// CHECK: class-template2.cpp:25:20: error: external variable 'x0r' declared with incompatible types in different translation units ('X0<double> *' vs. 'X0<float> *')
|
||||
// CHECK: class-template1.cpp:24:19: note: declared here with type 'X0<float> *'
|
||||
|
||||
// CHECK: class-template1.cpp:32:8: warning: type 'X0<wchar_t>' has incompatible definitions in different translation units
|
||||
// CHECK: class-template1.cpp:33:7: note: field 'member' has type 'int' here
|
||||
// CHECK: class-template2.cpp:34:9: note: field 'member' has type 'float' here
|
||||
|
||||
// CHECK: 1 warning and 5 errors generated.
|
||||
|
|
Loading…
Reference in New Issue