Cope with anonymous tags defined within declarators by structurally

comparing their types under the assumption that they are equivalent,
rather than importing the types and then checking for compatibility. A
few minor tweaks here:
  - Teach structural matching to handle compatibility between
  function types with prototypes and those without prototypes.
  - Teach structural matching that an incomplete record decl is the
  same as any other record decl with the same name.
  - Keep track of pairs of declarations that we have already checked
  (but failed to find as structurally matching), so we don't emit
  diagnostics repeatedly.
  - When importing a typedef of an anonymous tag, be sure to link the
  imported tag type to its typedef.

With these changes, we survive a repeated import of <stdlib.h> and
<stdio.h>. Alas, the ASTNodeImporter is getting a little grotty.

llvm-svn: 96298
This commit is contained in:
Douglas Gregor 2010-02-15 23:54:17 +00:00
parent a945c64b5a
commit b4964f7705
9 changed files with 195 additions and 83 deletions

View File

@ -18,6 +18,8 @@
#include "clang/AST/DeclarationName.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
namespace clang {
class ASTContext;
@ -34,6 +36,10 @@ namespace clang {
/// \brief Imports selected nodes from one AST context into another context,
/// merging AST nodes where appropriate.
class ASTImporter {
public:
typedef llvm::DenseSet<std::pair<Decl *, Decl *> > NonEquivalentDeclSet;
private:
/// \brief The contexts we're importing to and from.
ASTContext &ToContext, &FromContext;
@ -59,6 +65,14 @@ namespace clang {
/// manager to the corresponding FileIDs in the "to" source manager.
llvm::DenseMap<unsigned, FileID> ImportedFileIDs;
/// \brief Imported, anonymous tag declarations that are missing their
/// corresponding typedefs.
llvm::SmallVector<TagDecl *, 4> AnonTagsWithPendingTypedefs;
/// \brief Declaration (from, to) pairs that are known not to be equivalent
/// (which we have already complained about).
NonEquivalentDeclSet NonEquivalentDecls;
public:
ASTImporter(Diagnostic &Diags,
ASTContext &ToContext, FileManager &ToFileManager,
@ -202,11 +216,18 @@ namespace clang {
/// \brief Report a diagnostic in the "from" context.
DiagnosticBuilder FromDiag(SourceLocation Loc, unsigned DiagID);
/// \brief Return the set of declarations that we know are not equivalent.
NonEquivalentDeclSet &getNonEquivalentDecls() { return NonEquivalentDecls; }
/// \brief Note that we have imported the "from" declaration by mapping it
/// to the (potentially-newly-created) "to" declaration.
///
/// \returns \p To
Decl *Imported(Decl *From, Decl *To);
/// \brief Determine whether the given types are structurally
/// equivalent.
bool IsStructurallyEquivalent(QualType From, QualType To);
};
}

View File

@ -81,10 +81,6 @@ namespace {
bool ImportDeclParts(NamedDecl *D, DeclContext *&DC,
DeclContext *&LexicalDC, DeclarationName &Name,
SourceLocation &Loc);
bool ImportDeclParts(ValueDecl *D,
DeclContext *&DC, DeclContext *&LexicalDC,
DeclarationName &Name, SourceLocation &Loc,
QualType &T);
bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord);
bool IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToRecord);
Decl *VisitDecl(Decl *D);
@ -128,14 +124,20 @@ namespace {
/// with a declaration in the second context still needs to be verified.
std::deque<Decl *> DeclsToCheck;
/// \brief Declaration (from, to) pairs that are known not to be equivalent
/// (which we have already complained about).
llvm::DenseSet<std::pair<Decl *, Decl *> > &NonEquivalentDecls;
/// \brief Whether we're being strict about the spelling of types when
/// unifying two types.
bool StrictTypeSpelling;
StructuralEquivalenceContext(ASTContext &C1, ASTContext &C2,
Diagnostic &Diags,
llvm::DenseSet<std::pair<Decl *, Decl *> > &NonEquivalentDecls,
bool StrictTypeSpelling = false)
: C1(C1), C2(C2), Diags(Diags), StrictTypeSpelling(StrictTypeSpelling) { }
: C1(C1), C2(C2), Diags(Diags), NonEquivalentDecls(NonEquivalentDecls),
StrictTypeSpelling(StrictTypeSpelling) { }
/// \brief Determine whether the two declarations are structurally
/// equivalent.
@ -273,11 +275,23 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
if (T1.getQualifiers() != T2.getQualifiers())
return false;
if (T1->getTypeClass() != T2->getTypeClass())
return false;
Type::TypeClass TC = T1->getTypeClass();
switch (T1->getTypeClass()) {
case Type::Builtin:
if (T1->getTypeClass() != T2->getTypeClass()) {
// Compare function types with prototypes vs. without prototypes as if
// both did not have prototypes.
if (T1->getTypeClass() == Type::FunctionProto &&
T2->getTypeClass() == Type::FunctionNoProto)
TC = Type::FunctionNoProto;
else if (T1->getTypeClass() == Type::FunctionNoProto &&
T2->getTypeClass() == Type::FunctionProto)
TC = Type::FunctionNoProto;
else
return false;
}
switch (TC) {
case Type::Builtin:
// FIXME: Deal with Char_S/Char_U.
if (cast<BuiltinType>(T1)->getKind() != cast<BuiltinType>(T2)->getKind())
return false;
@ -641,6 +655,13 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
// Compare the definitions of these two records. If either or both are
// incomplete, we assume that they are equivalent.
D1 = D1->getDefinition();
D2 = D2->getDefinition();
if (!D1 || !D2)
return true;
if (CXXRecordDecl *D1CXX = dyn_cast<CXXRecordDecl>(D1)) {
if (CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(D2)) {
if (D1CXX->getNumBases() != D2CXX->getNumBases()) {
@ -834,6 +855,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Decl *D1, Decl *D2) {
// FIXME: Check for known structural equivalences via a callback of some sort.
// Check whether we already know that these two declarations are not
// structurally equivalent.
if (Context.NonEquivalentDecls.count(std::make_pair(D1->getCanonicalDecl(),
D2->getCanonicalDecl())))
return false;
// Determine whether we've already produced a tentative equivalence for D1.
Decl *&EquivToD1 = Context.TentativeEquivalences[D1->getCanonicalDecl()];
if (EquivToD1)
@ -870,6 +897,8 @@ bool StructuralEquivalenceContext::Finish() {
Decl *D2 = TentativeEquivalences[D1];
assert(D2 && "Unrecorded tentative equivalence?");
bool Equivalent = true;
// FIXME: Switch on all declaration kinds. For now, we're just going to
// check the obvious ones.
if (RecordDecl *Record1 = dyn_cast<RecordDecl>(D1)) {
@ -881,20 +910,14 @@ bool StructuralEquivalenceContext::Finish() {
IdentifierInfo *Name2 = Record2->getIdentifier();
if (!Name2 && Record2->getTypedefForAnonDecl())
Name2 = Record2->getTypedefForAnonDecl()->getIdentifier();
if (!::IsStructurallyEquivalent(Name1, Name2))
return true;
if (!::IsStructurallyEquivalent(*this, Record1, Record2))
return true;
if (!::IsStructurallyEquivalent(Name1, Name2) ||
!::IsStructurallyEquivalent(*this, Record1, Record2))
Equivalent = false;
} else {
// Record/non-record mismatch.
return true;
Equivalent = false;
}
continue;
}
if (EnumDecl *Enum1 = dyn_cast<EnumDecl>(D1)) {
} else if (EnumDecl *Enum1 = dyn_cast<EnumDecl>(D1)) {
if (EnumDecl *Enum2 = dyn_cast<EnumDecl>(D2)) {
// Check for equivalent enum names.
IdentifierInfo *Name1 = Enum1->getIdentifier();
@ -903,37 +926,34 @@ bool StructuralEquivalenceContext::Finish() {
IdentifierInfo *Name2 = Enum2->getIdentifier();
if (!Name2 && Enum2->getTypedefForAnonDecl())
Name2 = Enum2->getTypedefForAnonDecl()->getIdentifier();
if (!::IsStructurallyEquivalent(Name1, Name2))
return true;
if (!::IsStructurallyEquivalent(*this, Enum1, Enum2))
return true;
if (!::IsStructurallyEquivalent(Name1, Name2) ||
!::IsStructurallyEquivalent(*this, Enum1, Enum2))
Equivalent = false;
} else {
// Enum/non-enum mismatch
return true;
Equivalent = false;
}
continue;
}
if (TypedefDecl *Typedef1 = dyn_cast<TypedefDecl>(D1)) {
} else if (TypedefDecl *Typedef1 = dyn_cast<TypedefDecl>(D1)) {
if (TypedefDecl *Typedef2 = dyn_cast<TypedefDecl>(D2)) {
if (!::IsStructurallyEquivalent(Typedef1->getIdentifier(),
Typedef2->getIdentifier()))
return true;
if (!::IsStructurallyEquivalent(*this,
Typedef2->getIdentifier()) ||
!::IsStructurallyEquivalent(*this,
Typedef1->getUnderlyingType(),
Typedef2->getUnderlyingType()))
return true;
Equivalent = false;
} else {
// Typedef/non-typedef mismatch.
return true;
Equivalent = false;
}
continue;
}
if (!Equivalent) {
// Note that these two declarations are not equivalent (and we already
// know about it).
NonEquivalentDecls.insert(std::make_pair(D1->getCanonicalDecl(),
D2->getCanonicalDecl()));
return true;
}
// FIXME: Check other declaration kinds!
}
@ -1335,35 +1355,20 @@ bool ASTNodeImporter::ImportDeclParts(NamedDecl *D, DeclContext *&DC,
return false;
}
bool ASTNodeImporter::ImportDeclParts(ValueDecl *D,
DeclContext *&DC,
DeclContext *&LexicalDC,
DeclarationName &Name,
SourceLocation &Loc,
QualType &T) {
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc))
return true;
// Import the type of this declaration.
T = Importer.Import(D->getType());
if (T.isNull())
return true;
return false;
}
bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord,
RecordDecl *ToRecord) {
StructuralEquivalenceContext SEC(Importer.getFromContext(),
Importer.getToContext(),
Importer.getDiags());
Importer.getDiags(),
Importer.getNonEquivalentDecls());
return SEC.IsStructurallyEquivalent(FromRecord, ToRecord);
}
bool ASTNodeImporter::IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToEnum) {
StructuralEquivalenceContext SEC(Importer.getFromContext(),
Importer.getToContext(),
Importer.getDiags());
Importer.getDiags(),
Importer.getNonEquivalentDecls());
return SEC.IsStructurallyEquivalent(FromEnum, ToEnum);
}
@ -1381,11 +1386,6 @@ Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) {
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc))
return 0;
// Import the underlying type of this typedef;
QualType T = Importer.Import(D->getUnderlyingType());
if (T.isNull())
return 0;
// If this typedef is not in block scope, determine whether we've
// seen a typedef with the same name (that we can merge with) or any
// other entity by that name (which name lookup could conflict with).
@ -1398,8 +1398,8 @@ Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) {
if (!(*Lookup.first)->isInIdentifierNamespace(IDNS))
continue;
if (TypedefDecl *FoundTypedef = dyn_cast<TypedefDecl>(*Lookup.first)) {
if (Importer.getToContext().typesAreCompatible(T,
FoundTypedef->getUnderlyingType()))
if (Importer.IsStructurallyEquivalent(D->getUnderlyingType(),
FoundTypedef->getUnderlyingType()))
return Importer.Imported(D, FoundTypedef);
}
@ -1415,6 +1415,11 @@ Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) {
}
}
// Import the underlying type of this typedef;
QualType T = Importer.Import(D->getUnderlyingType());
if (T.isNull())
return 0;
// Create the new typedef node.
TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo());
TypedefDecl *ToTypedef = TypedefDecl::Create(Importer.getToContext(), DC,
@ -1423,6 +1428,7 @@ Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) {
ToTypedef->setLexicalDeclContext(LexicalDC);
Importer.Imported(D, ToTypedef);
LexicalDC->addDecl(ToTypedef);
return ToTypedef;
}
@ -1648,10 +1654,13 @@ Decl *ASTNodeImporter::VisitEnumConstantDecl(EnumConstantDecl *D) {
DeclContext *DC, *LexicalDC;
DeclarationName Name;
SourceLocation Loc;
QualType T;
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc, T))
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc))
return 0;
QualType T = Importer.Import(D->getType());
if (T.isNull())
return 0;
// Determine whether there are any other declarations with the same name and
// in the same context.
if (!LexicalDC->isFunctionOrMethod()) {
@ -1693,9 +1702,8 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// Import the major distinguishing characteristics of this function.
DeclContext *DC, *LexicalDC;
DeclarationName Name;
QualType T;
SourceLocation Loc;
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc, T))
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc))
return 0;
// Try to find a function in our own ("to") context with the same name, same
@ -1712,8 +1720,8 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
if (FunctionDecl *FoundFunction = dyn_cast<FunctionDecl>(*Lookup.first)) {
if (isExternalLinkage(FoundFunction->getLinkage()) &&
isExternalLinkage(D->getLinkage())) {
if (Importer.getToContext().typesAreCompatible(T,
FoundFunction->getType())) {
if (Importer.IsStructurallyEquivalent(D->getType(),
FoundFunction->getType())) {
// FIXME: Actually try to merge the body and other attributes.
return Importer.Imported(D, FoundFunction);
}
@ -1727,7 +1735,7 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// Complain about inconsistent function types.
Importer.ToDiag(Loc, diag::err_odr_function_type_inconsistent)
<< Name << T << FoundFunction->getType();
<< Name << D->getType() << FoundFunction->getType();
Importer.ToDiag(FoundFunction->getLocation(),
diag::note_odr_value_here)
<< FoundFunction->getType();
@ -1745,6 +1753,11 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
return 0;
}
}
// Import the type.
QualType T = Importer.Import(D->getType());
if (T.isNull())
return 0;
// Import the function parameters.
llvm::SmallVector<ParmVarDecl *, 8> Parameters;
@ -1784,9 +1797,13 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
// Import the major distinguishing characteristics of a variable.
DeclContext *DC, *LexicalDC;
DeclarationName Name;
QualType T;
SourceLocation Loc;
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc, T))
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc))
return 0;
// Import the type.
QualType T = Importer.Import(D->getType());
if (T.isNull())
return 0;
TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo());
@ -1807,9 +1824,8 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
// Import the major distinguishing characteristics of a variable.
DeclContext *DC, *LexicalDC;
DeclarationName Name;
QualType T;
SourceLocation Loc;
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc, T))
if (ImportDeclParts(D, DC, LexicalDC, Name, Loc))
return 0;
// Try to find a variable in our own ("to") context with the same name and
@ -1828,8 +1844,8 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
// We have found a variable that we may need to merge with. Check it.
if (isExternalLinkage(FoundVar->getLinkage()) &&
isExternalLinkage(D->getLinkage())) {
if (Importer.getToContext().typesAreCompatible(T,
FoundVar->getType())) {
if (Importer.IsStructurallyEquivalent(D->getType(),
FoundVar->getType())) {
MergeWithVar = FoundVar;
break;
}
@ -1837,10 +1853,15 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
const ArrayType *FoundArray
= Importer.getToContext().getAsArrayType(FoundVar->getType());
const ArrayType *TArray
= Importer.getToContext().getAsArrayType(T);
= Importer.getToContext().getAsArrayType(D->getType());
if (FoundArray && TArray) {
if (isa<IncompleteArrayType>(FoundArray) &&
isa<ConstantArrayType>(TArray)) {
// Import the type.
QualType T = Importer.Import(D->getType());
if (T.isNull())
return 0;
FoundVar->setType(T);
MergeWithVar = FoundVar;
break;
@ -1852,7 +1873,7 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
}
Importer.ToDiag(Loc, diag::err_odr_variable_type_inconsistent)
<< Name << T << FoundVar->getType();
<< Name << D->getType() << FoundVar->getType();
Importer.ToDiag(FoundVar->getLocation(), diag::note_odr_value_here)
<< FoundVar->getType();
}
@ -1890,6 +1911,11 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
}
}
// Import the type.
QualType T = Importer.Import(D->getType());
if (T.isNull())
return 0;
// Create the imported variable.
TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo());
VarDecl *ToVar = VarDecl::Create(Importer.getToContext(), DC, Loc,
@ -2045,6 +2071,29 @@ Decl *ASTImporter::Import(Decl *FromD) {
// Record the imported declaration.
ImportedDecls[FromD] = ToD;
if (TagDecl *FromTag = dyn_cast<TagDecl>(FromD)) {
// Keep track of anonymous tags that have an associated typedef.
if (FromTag->getTypedefForAnonDecl())
AnonTagsWithPendingTypedefs.push_back(FromTag);
} else if (TypedefDecl *FromTypedef = dyn_cast<TypedefDecl>(FromD)) {
// When we've finished transforming a typedef, see whether it was the
// typedef for an anonymous tag.
for (llvm::SmallVector<TagDecl *, 4>::iterator
FromTag = AnonTagsWithPendingTypedefs.begin(),
FromTagEnd = AnonTagsWithPendingTypedefs.end();
FromTag != FromTagEnd; ++FromTag) {
if ((*FromTag)->getTypedefForAnonDecl() == FromTypedef) {
if (TagDecl *ToTag = cast_or_null<TagDecl>(Import(*FromTag))) {
// We found the typedef for an anonymous tag; link them.
ToTag->setTypedefForAnonDecl(cast<TypedefDecl>(ToD));
AnonTagsWithPendingTypedefs.erase(FromTag);
break;
}
}
}
}
return ToD;
}
@ -2236,3 +2285,14 @@ Decl *ASTImporter::Imported(Decl *From, Decl *To) {
ImportedDecls[From] = To;
return To;
}
bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To) {
llvm::DenseMap<Type *, Type *>::iterator Pos
= ImportedTypes.find(From.getTypePtr());
if (Pos != ImportedTypes.end() && ToContext.hasSameType(Import(From), To))
return true;
StructuralEquivalenceContext SEC(FromContext, ToContext, Diags,
NonEquivalentDecls);
return SEC.IsStructurallyEquivalent(From, To);
}

View File

@ -32,3 +32,11 @@ enum E5 {
E5Enumerator2,
E5Enumerator3
} x5;
// Matching, with typedef
typedef enum {
E6Enumerator1,
E6Enumerator2
} E6;
E6 x6;

View File

@ -33,3 +33,10 @@ enum E5 {
E5Enumerator4
} x5;
// Matching, with typedef
typedef enum {
E6Enumerator1,
E6Enumerator2
} E6;
E6 x6;

View File

@ -3,4 +3,4 @@ void f1(int, float);
void f2();
void f3(void);
void f4(int, int);
int f5(int) __attribute__((const));

View File

@ -4,3 +4,4 @@ void f1(Int, double);
void f2(int, int);
void f3(int);
static void f4(float, float);
int f5(int) __attribute__((const));

View File

@ -55,3 +55,9 @@ struct DeepError {
int value;
struct DeeperError { int i; int f; } *Deeper;
} xDeep;
// Matches
struct {
Int i;
float f;
} x11;

View File

@ -52,3 +52,9 @@ struct DeepError {
int value;
struct DeeperError { int i; float f; } *Deeper;
} xDeep;
// Matches
struct {
int i;
float f;
} x11;

View File

@ -34,6 +34,9 @@
// CHECK: struct1.c:56:10: warning: type 'struct DeeperError' has incompatible definitions in different translation units
// CHECK: struct1.c:56:35: note: field 'f' has type 'int' here
// CHECK: struct2.c:53:37: note: field 'f' has type 'float' here
// CHECK: struct1.c:54:8: warning: type 'struct DeepError' has incompatible definitions in different translation units
// CHECK: struct1.c:56:41: note: field 'Deeper' has type 'struct DeeperError *' here
// CHECK: struct2.c:53:43: note: field 'Deeper' has type 'struct DeeperError *' here
// CHECK: struct2.c:54:3: error: external variable 'xDeep' declared with incompatible types in different translation units ('struct DeepError' vs. 'struct DeepError')
// CHECK: struct1.c:57:3: note: declared here with type 'struct DeepError'
// CHECK: 37 diagnostics