diff --git a/include/clang/AST/ASTImporter.h b/include/clang/AST/ASTImporter.h index bdc1a51e98..b1ead16925 100644 --- a/include/clang/AST/ASTImporter.h +++ b/include/clang/AST/ASTImporter.h @@ -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 ImportedDecls; - + + /// \brief Mapping from the already-imported statements in the "from" + /// context to the corresponding statements in the "to" context. + llvm::DenseMap ImportedStmts; + /// \brief Mapping from the already-imported FileIDs in the "from" source /// manager to the corresponding FileIDs in the "to" source manager. llvm::DenseMap 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 &getImportedDecls() { return ImportedDecls; } diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index b1d3920ae0..cfc2bfdf43 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -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">; } diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 62cc339254..a7a76532ca 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -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, - public DeclVisitor { + public DeclVisitor, + public StmtVisitor { ASTImporter &Importer; public: @@ -36,6 +38,7 @@ namespace { using TypeVisitor::Visit; using DeclVisitor::Visit; + using StmtVisitor::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(FromRecord)) { if (CXXRecordDecl *ToCXX = dyn_cast(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(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::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); } diff --git a/lib/Frontend/ASTMerge.cpp b/lib/Frontend/ASTMerge.cpp index fd34ddae12..fbb87669d9 100644 --- a/lib/Frontend/ASTMerge.cpp +++ b/lib/Frontend/ASTMerge.cpp @@ -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(), diff --git a/test/ASTMerge/Inputs/struct1.c b/test/ASTMerge/Inputs/struct1.c index 381f00a0a6..5743fe0f48 100644 --- a/test/ASTMerge/Inputs/struct1.c +++ b/test/ASTMerge/Inputs/struct1.c @@ -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; diff --git a/test/ASTMerge/Inputs/struct2.c b/test/ASTMerge/Inputs/struct2.c index 0a90609006..a707ed2a5b 100644 --- a/test/ASTMerge/Inputs/struct2.c +++ b/test/ASTMerge/Inputs/struct2.c @@ -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; diff --git a/test/ASTMerge/struct.c b/test/ASTMerge/struct.c index 99fa74b10d..c6e57185ab 100644 --- a/test/ASTMerge/struct.c +++ b/test/ASTMerge/struct.c @@ -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