When AST merging for record declarations fails, warn about the

incompatibility and show where the structural differences are. For
example:

struct1.c:36:8: warning: type 'struct S7' has incompatible definitions
in different translation units
struct S7 { int i : 8; unsigned j : 8; } x7;
       ^
struct1.c:36:33: note: bit-field 'j' with type 'unsigned int' and length 8 here
struct S7 { int i : 8; unsigned j : 8; } x7;
                                ^
struct2.c:33:33: note: bit-field 'j' with type 'unsigned int' and length 16 here
struct S7 { int i : 8; unsigned j : 16; } x7;
                                ^

There are a few changes to make this work:
  - ASTImporter now has only a single Diagnostic object, not multiple
  diagnostic objects. Otherwise, having a warning/error printed via
  one Diagnostic and its note printed on the other Diagnostic could
  cause the note to be suppressed.
  - Implemented import functionality for IntegerLiteral (along with
  general support for statements and expressions)

llvm-svn: 95900
This commit is contained in:
Douglas Gregor 2010-02-11 19:21:55 +00:00
parent 8a30324e51
commit 7eeb59752a
7 changed files with 288 additions and 63 deletions

View File

@ -40,9 +40,8 @@ namespace clang {
/// \brief The file managers we're importing to and from.
FileManager &ToFileManager, &FromFileManager;
/// \brief The diagnostics object that we should use to emit diagnostics
/// within the context we're importing to and from.
Diagnostic &ToDiags, &FromDiags;
/// \brief The diagnostics object that we should use to emit diagnostics.
Diagnostic &Diags;
/// \brief Mapping from the already-imported types in the "from" context
/// to the corresponding types in the "to" context.
@ -51,16 +50,19 @@ namespace clang {
/// \brief Mapping from the already-imported declarations in the "from"
/// context to the corresponding declarations in the "to" context.
llvm::DenseMap<Decl *, Decl *> ImportedDecls;
/// \brief Mapping from the already-imported statements in the "from"
/// context to the corresponding statements in the "to" context.
llvm::DenseMap<Stmt *, Stmt *> ImportedStmts;
/// \brief Mapping from the already-imported FileIDs in the "from" source
/// manager to the corresponding FileIDs in the "to" source manager.
llvm::DenseMap<unsigned, FileID> ImportedFileIDs;
public:
ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
Diagnostic &ToDiags,
ASTContext &FromContext, FileManager &FromFileManager,
Diagnostic &FromDiags);
ASTImporter(Diagnostic &Diags,
ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager);
virtual ~ASTImporter();
@ -191,14 +193,6 @@ namespace clang {
/// \brief Retrieve the file manager that AST nodes are being imported from.
FileManager &getFromFileManager() const { return FromFileManager; }
/// \brief Retrieve the diagnostics object to use to report errors within
/// the context we're importing into.
Diagnostic &getToDiags() const { return ToDiags; }
/// \brief Retrieve the diagnostics object to use to report errors within
/// the context we're importing from.
Diagnostic &getFromDiags() const { return FromDiags; }
/// \brief Retrieve the mapping from declarations in the "from" context
/// to the already-imported declarations in the "to" context.
llvm::DenseMap<Decl *, Decl *> &getImportedDecls() { return ImportedDecls; }

View File

@ -37,5 +37,19 @@ def note_odr_defined_here : Note<"also defined here">;
def err_odr_function_type_inconsistent : Error<
"external function %0 declared with incompatible types in different "
"translation units (%1 vs. %2)">;
def warn_odr_class_type_inconsistent : Warning<
"type %0 has incompatible definitions in different translation units">;
def note_odr_tag_kind_here: Note<
"%0 is a %select{struct|union|class|enum}1 here">;
def note_odr_field : Note<"field %0 has type %1 here">;
def note_odr_missing_field : Note<"no corresponding field here">;
def note_odr_bit_field : Note<"bit-field %0 with type %1 and length %2 here">;
def note_odr_not_bit_field : Note<"field %0 is not a bit-field">;
def note_odr_base : Note<"class has base type %0">;
def note_odr_virtual_base : Note<
"%select{non-virtual|virtual}0 derivation here">;
def note_odr_missing_base : Note<"no corresponding base class here">;
def note_odr_number_of_bases : Note<
"class has %0 base %plural{1:class|:classes}0">;
def err_unsupported_ast_node: Error<"cannot import unsupported AST node %0">;
}

View File

@ -18,6 +18,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/FileManager.h"
@ -28,7 +29,8 @@ using namespace clang;
namespace {
class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, QualType>,
public DeclVisitor<ASTNodeImporter, Decl *> {
public DeclVisitor<ASTNodeImporter, Decl *>,
public StmtVisitor<ASTNodeImporter, Stmt *> {
ASTImporter &Importer;
public:
@ -36,6 +38,7 @@ namespace {
using TypeVisitor<ASTNodeImporter, QualType>::Visit;
using DeclVisitor<ASTNodeImporter, Decl *>::Visit;
using StmtVisitor<ASTNodeImporter, Stmt *>::Visit;
// Importing types
QualType VisitType(Type *T);
@ -89,6 +92,13 @@ namespace {
Decl *VisitFieldDecl(FieldDecl *D);
Decl *VisitVarDecl(VarDecl *D);
Decl *VisitParmVarDecl(ParmVarDecl *D);
// Importing statements
Stmt *VisitStmt(Stmt *S);
// Importing expressions
Expr *VisitExpr(Expr *E);
Expr *VisitIntegerLiteral(IntegerLiteral *E);
};
}
@ -506,37 +516,81 @@ bool ASTNodeImporter::ImportDeclParts(DeclaratorDecl *D,
bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord,
RecordDecl *ToRecord) {
// FIXME: If we know that the two records are the same according to the ODR,
// we could diagnose structural mismatches here. However, we don't really
// have that information.
if (FromRecord->isUnion() != ToRecord->isUnion())
if (FromRecord->isUnion() != ToRecord->isUnion()) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.FromDiag(FromRecord->getLocation(), diag::note_odr_tag_kind_here)
<< FromRecord->getDeclName() << (unsigned)FromRecord->getTagKind();
return false;
}
if (CXXRecordDecl *FromCXX = dyn_cast<CXXRecordDecl>(FromRecord)) {
if (CXXRecordDecl *ToCXX = dyn_cast<CXXRecordDecl>(ToRecord)) {
if (FromCXX->getNumBases() != ToCXX->getNumBases())
if (FromCXX->getNumBases() != ToCXX->getNumBases()) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.ToDiag(ToRecord->getLocation(), diag::note_odr_number_of_bases)
<< ToCXX->getNumBases();
Importer.FromDiag(FromRecord->getLocation(),
diag::note_odr_number_of_bases)
<< FromCXX->getNumBases();
return false;
}
// Check the base classes.
for (CXXRecordDecl::base_class_iterator FromBase = FromCXX->bases_begin(),
FromBaseEnd = FromCXX->bases_end(),
ToBase = ToCXX->bases_begin();
FromBase != FromBaseEnd;
++FromBase, ++ToBase) {
// Check virtual vs. non-virtual inheritance mismatch.
if (FromBase->isVirtual() != ToBase->isVirtual())
return false;
++FromBase, ++ToBase) {
// Check the type we're inheriting from.
QualType FromBaseT = Importer.Import(FromBase->getType());
if (FromBaseT.isNull())
return false;
if (!Importer.getToContext().typesAreCompatible(FromBaseT,
ToBase->getType()))
ToBase->getType())) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.ToDiag(ToBase->getSourceRange().getBegin(),
diag::note_odr_base)
<< ToBase->getType()
<< ToBase->getSourceRange();
Importer.FromDiag(FromBase->getSourceRange().getBegin(),
diag::note_odr_base)
<< FromBase->getType()
<< FromBase->getSourceRange();
return false;
}
// Check virtual vs. non-virtual inheritance mismatch.
if (FromBase->isVirtual() != ToBase->isVirtual()) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.ToDiag(ToBase->getSourceRange().getBegin(),
diag::note_odr_virtual_base)
<< ToBase->isVirtual() << ToBase->getSourceRange();
Importer.FromDiag(FromBase->getSourceRange().getBegin(),
diag::note_odr_base)
<< FromBase->isVirtual()
<< FromBase->getSourceRange();
return false;
}
}
} else if (FromCXX->getNumBases() > 0) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
const CXXBaseSpecifier *FromBase = FromCXX->bases_begin();
Importer.FromDiag(FromBase->getSourceRange().getBegin(),
diag::note_odr_base)
<< FromBase->getType()
<< FromBase->getSourceRange();
Importer.ToDiag(ToRecord->getLocation(), diag::note_odr_missing_base);
return false;
}
}
@ -548,19 +602,58 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord,
FromFieldEnd = FromRecord->field_end();
FromField != FromFieldEnd;
++FromField, ++ToField) {
if (ToField == ToFieldEnd)
if (ToField == ToFieldEnd) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.FromDiag(FromField->getLocation(), diag::note_odr_field)
<< FromField->getDeclName() << FromField->getType();
Importer.ToDiag(ToRecord->getLocation(), diag::note_odr_missing_field);
return false;
}
QualType FromT = Importer.Import(FromField->getType());
if (FromT.isNull())
return false;
if (FromField->isBitField() != ToField->isBitField())
if (!Importer.getToContext().typesAreCompatible(FromT, ToField->getType())){
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.ToDiag(ToField->getLocation(), diag::note_odr_field)
<< ToField->getDeclName() << ToField->getType();
Importer.FromDiag(FromField->getLocation(), diag::note_odr_field)
<< FromField->getDeclName() << FromField->getType();
return false;
}
if (!Importer.getToContext().typesAreCompatible(FromT, ToField->getType()))
if (FromField->isBitField() != ToField->isBitField()) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
if (FromField->isBitField()) {
llvm::APSInt Bits;
FromField->getBitWidth()->isIntegerConstantExpr(Bits,
Importer.getFromContext());
Importer.FromDiag(FromField->getLocation(), diag::note_odr_bit_field)
<< FromField->getDeclName() << FromField->getType()
<< Bits.toString(10, false);
Importer.ToDiag(ToField->getLocation(), diag::note_odr_not_bit_field)
<< ToField->getDeclName();
} else {
llvm::APSInt Bits;
ToField->getBitWidth()->isIntegerConstantExpr(Bits,
Importer.getToContext());
Importer.ToDiag(ToField->getLocation(), diag::note_odr_bit_field)
<< ToField->getDeclName() << ToField->getType()
<< Bits.toString(10, false);
Importer.FromDiag(FromField->getLocation(),
diag::note_odr_not_bit_field)
<< FromField->getDeclName();
}
return false;
}
if (FromField->isBitField()) {
// Make sure that the bit-fields are the same length.
llvm::APSInt FromBits, ToBits;
@ -579,12 +672,32 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord,
FromBits.setIsUnsigned(true);
ToBits.setIsUnsigned(true);
if (FromBits != ToBits)
if (FromBits != ToBits) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.ToDiag(ToField->getLocation(), diag::note_odr_bit_field)
<< ToField->getDeclName() << ToField->getType()
<< ToBits.toString(10, false);
Importer.FromDiag(FromField->getLocation(), diag::note_odr_bit_field)
<< FromField->getDeclName() << FromField->getType()
<< FromBits.toString(10, false);
return false;
}
}
}
return ToField == ToFieldEnd;
if (ToField != ToFieldEnd) {
Importer.ToDiag(ToRecord->getLocation(),
diag::warn_odr_class_type_inconsistent)
<< Importer.getToContext().getTypeDeclType(ToRecord);
Importer.ToDiag(ToField->getLocation(), diag::note_odr_field)
<< ToField->getDeclName() << ToField->getType();
Importer.FromDiag(FromRecord->getLocation(), diag::note_odr_missing_field);
return false;
}
return true;
}
Decl *ASTNodeImporter::VisitDecl(Decl *D) {
@ -691,11 +804,14 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
}
if (RecordDecl *FoundRecord = dyn_cast<RecordDecl>(Found)) {
if (IsStructuralMatch(D, FoundRecord)) {
RecordDecl *FoundDef = FoundRecord->getDefinition();
// FIXME: If we found something but there is no definition,
// assume the types are the same and fill in the gaps.
if (FoundDef && IsStructuralMatch(D, FoundDef)) {
// The record types structurally match.
// FIXME: For C++, we should also merge methods here.
Importer.getImportedDecls()[D] = FoundRecord;
return FoundRecord;
Importer.getImportedDecls()[D] = FoundDef;
return FoundDef;
}
}
@ -1026,13 +1142,40 @@ Decl *ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) {
return ToParm;
}
ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
Diagnostic &ToDiags,
ASTContext &FromContext, FileManager &FromFileManager,
Diagnostic &FromDiags)
//----------------------------------------------------------------------------
// Import Statements
//----------------------------------------------------------------------------
Stmt *ASTNodeImporter::VisitStmt(Stmt *S) {
Importer.FromDiag(S->getLocStart(), diag::err_unsupported_ast_node)
<< S->getStmtClassName();
return 0;
}
//----------------------------------------------------------------------------
// Import Expressions
//----------------------------------------------------------------------------
Expr *ASTNodeImporter::VisitExpr(Expr *E) {
Importer.FromDiag(E->getLocStart(), diag::err_unsupported_ast_node)
<< E->getStmtClassName();
return 0;
}
Expr *ASTNodeImporter::VisitIntegerLiteral(IntegerLiteral *E) {
QualType T = Importer.Import(E->getType());
if (T.isNull())
return 0;
return new (Importer.getToContext())
IntegerLiteral(E->getValue(), T, Importer.Import(E->getLocation()));
}
ASTImporter::ASTImporter(Diagnostic &Diags,
ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager)
: ToContext(ToContext), FromContext(FromContext),
ToFileManager(ToFileManager), FromFileManager(FromFileManager),
ToDiags(ToDiags), FromDiags(FromDiags) {
Diags(Diags) {
ImportedDecls[FromContext.getTranslationUnitDecl()]
= ToContext.getTranslationUnitDecl();
}
@ -1114,8 +1257,20 @@ Stmt *ASTImporter::Import(Stmt *FromS) {
if (!FromS)
return 0;
// FIXME: Implement!
return 0;
// Check whether we've already imported this declaration.
llvm::DenseMap<Stmt *, Stmt *>::iterator Pos = ImportedStmts.find(FromS);
if (Pos != ImportedStmts.end())
return Pos->second;
// Import the type
ASTNodeImporter Importer(*this);
Stmt *ToS = Importer.Visit(FromS);
if (!ToS)
return 0;
// Record the imported declaration.
ImportedStmts[FromS] = ToS;
return ToS;
}
NestedNameSpecifier *ASTImporter::Import(NestedNameSpecifier *FromNNS) {
@ -1259,11 +1414,11 @@ DeclarationName ASTImporter::HandleNameConflict(DeclarationName Name,
}
DiagnosticBuilder ASTImporter::ToDiag(SourceLocation Loc, unsigned DiagID) {
return ToDiags.Report(FullSourceLoc(Loc, ToContext.getSourceManager()),
DiagID);
return Diags.Report(FullSourceLoc(Loc, ToContext.getSourceManager()),
DiagID);
}
DiagnosticBuilder ASTImporter::FromDiag(SourceLocation Loc, unsigned DiagID) {
return FromDiags.Report(FullSourceLoc(Loc, FromContext.getSourceManager()),
DiagID);
return Diags.Report(FullSourceLoc(Loc, FromContext.getSourceManager()),
DiagID);
}

View File

@ -37,21 +37,16 @@ void ASTMergeAction::ExecuteAction() {
CI.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument,
&CI.getASTContext());
for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) {
Diagnostic ASTDiags(CI.getDiagnostics().getClient());
ASTUnit *Unit = ASTUnit::LoadFromPCHFile(ASTFiles[I], ASTDiags,
ASTUnit *Unit = ASTUnit::LoadFromPCHFile(ASTFiles[I], CI.getDiagnostics(),
false, true);
if (!Unit)
continue;
ASTDiags.SetArgToStringFn(&FormatASTNodeDiagnosticArgument,
&Unit->getASTContext());
ASTImporter Importer(CI.getASTContext(),
ASTImporter Importer(CI.getDiagnostics(),
CI.getASTContext(),
CI.getFileManager(),
CI.getDiagnostics(),
Unit->getASTContext(),
Unit->getFileManager(),
ASTDiags);
Unit->getFileManager());
TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
for (DeclContext::decl_iterator D = TU->decls_begin(),

View File

@ -1,6 +1,7 @@
typedef int Int;
typedef float Float;
// Matches
struct S0 {
Int field1;
Float field2;
@ -8,9 +9,28 @@ struct S0 {
struct S0 x0;
// Mismatch in field type
struct S1 {
Int field1;
int field2;
};
struct S1 x1;
// Mismatch in tag kind.
struct S2 { int i; float f; } x2;
// Missing fields
struct S3 { int i; float f; double d; } x3;
// Extra fields
struct S4 { int i; } x4;
// Bit-field matches
struct S5 { int i : 8; unsigned j : 8; } x5;
// Bit-field mismatch
struct S6 { int i : 8; unsigned j : 8; } x6;
// Bit-field mismatch
struct S7 { int i : 8; unsigned j : 8; } x7;

View File

@ -1,3 +1,4 @@
// Matches
struct S0 {
int field1;
float field2;
@ -5,9 +6,28 @@ struct S0 {
struct S0 x0;
// Mismatch in field type
struct S1 {
int field1;
float field2;
};
struct S1 x1;
// Mismatch in tag kind.
union S2 { int i; float f; } x2;
// Missing fields
struct S3 { int i; float f; } x3;
// Extra fields
struct S4 { int i; float f; } x4;
// Bit-field matches
struct S5 { int i : 8; unsigned j : 8; } x5;
// Bit-field mismatch
struct S6 { int i : 8; unsigned j; } x6;
// Bit-field mismatch
struct S7 { int i : 8; unsigned j : 16; } x7;

View File

@ -2,6 +2,33 @@
// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/struct2.c
// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s
// CHECK: struct2.c:13:11: error: external variable 'x1' declared with incompatible types in different translation units ('struct S1' vs. 'struct S1')
// CHECK: struct1.c:16:11: note: declared here with type 'struct S1'
// CHECK: 2 diagnostics
// CHECK: struct1.c:13:8: warning: type 'struct S1' has incompatible definitions in different translation units
// CHECK: struct1.c:15:7: note: field 'field2' has type 'int' here
// CHECK: struct2.c:12:9: note: field 'field2' has type 'float' here
// CHECK: struct2.c:15:11: error: external variable 'x1' declared with incompatible types in different translation units ('struct S1' vs. 'struct S1')
// CHECK: struct1.c:18:11: note: declared here with type 'struct S1'
// CHECK: struct1.c:21:8: warning: type 'struct S2' has incompatible definitions in different translation units
// CHECK: struct2.c:18:7: note: 'S2' is a union here
// CHECK: struct2.c:18:30: error: external variable 'x2' declared with incompatible types in different translation units ('union S2' vs. 'struct S2')
// CHECK: struct1.c:21:31: note: declared here with type 'struct S2'
// CHECK: struct1.c:24:8: warning: type 'struct S3' has incompatible definitions in different translation units
// CHECK: struct1.c:24:36: note: field 'd' has type 'double' here
// CHECK: struct2.c:21:8: note: no corresponding field here
// CHECK: struct2.c:21:31: error: external variable 'x3' declared with incompatible types in different translation units ('struct S3' vs. 'struct S3')
// CHECK: struct1.c:24:41: note: declared here with type 'struct S3'
// CHECK: struct1.c:27:8: warning: type 'struct S4' has incompatible definitions in different translation units
// CHECK: struct2.c:24:26: note: field 'f' has type 'float' here
// CHECK: struct1.c:27:8: note: no corresponding field here
// CHECK: struct2.c:24:31: error: external variable 'x4' declared with incompatible types in different translation units ('struct S4' vs. 'struct S4')
// CHECK: struct1.c:27:22: note: declared here with type 'struct S4'
// CHECK: struct1.c:33:8: warning: type 'struct S6' has incompatible definitions in different translation units
// CHECK: struct1.c:33:33: note: bit-field 'j' with type 'unsigned int' and length 8 here
// CHECK: struct2.c:30:33: note: field 'j' is not a bit-field
// CHECK: struct2.c:30:38: error: external variable 'x6' declared with incompatible types in different translation units ('struct S6' vs. 'struct S6')
// CHECK: struct1.c:33:42: note: declared here with type 'struct S6'
// CHECK: struct1.c:36:8: warning: type 'struct S7' has incompatible definitions in different translation units
// CHECK: struct1.c:36:33: note: bit-field 'j' with type 'unsigned int' and length 8 here
// CHECK: struct2.c:33:33: note: bit-field 'j' with type 'unsigned int' and length 16 here
// CHECK: struct2.c:33:43: error: external variable 'x7' declared with incompatible types in different translation units ('struct S7' vs. 'struct S7')
// CHECK: struct1.c:36:42: note: declared here with type 'struct S7'
// CHECK: 29 diagnostics