From: Douglas Gregor Date: Fri, 12 Feb 2010 22:17:39 +0000 (+0000) Subject: Implement AST importing and merging for enumeration types and X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=36ead2e992abb30aa3b4a40b4c8cb22cc9389fef;p=clang Implement AST importing and merging for enumeration types and enumerators, along with ImplicitCastExprs to make it work. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96024 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index cfc2bfdf43..b6c2b13895 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -37,7 +37,7 @@ 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< +def warn_odr_tag_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">; @@ -51,5 +51,8 @@ def note_odr_virtual_base : Note< 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 note_odr_enumerator : Note<"enumerator %0 with value %1 here">; +def note_odr_missing_enumerator : Note<"no corresponding enumerator here">; + 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 ea5b68f162..7b00b9f73b 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -80,14 +80,17 @@ namespace { bool ImportDeclParts(NamedDecl *D, DeclContext *&DC, DeclContext *&LexicalDC, DeclarationName &Name, SourceLocation &Loc); - bool ImportDeclParts(DeclaratorDecl *D, + 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 *ToEnum); Decl *VisitDecl(Decl *D); Decl *VisitTypedefDecl(TypedefDecl *D); + Decl *VisitEnumDecl(EnumDecl *D); Decl *VisitRecordDecl(RecordDecl *D); + Decl *VisitEnumConstantDecl(EnumConstantDecl *D); Decl *VisitFunctionDecl(FunctionDecl *D); Decl *VisitFieldDecl(FieldDecl *D); Decl *VisitVarDecl(VarDecl *D); @@ -99,6 +102,7 @@ namespace { // Importing expressions Expr *VisitExpr(Expr *E); Expr *VisitIntegerLiteral(IntegerLiteral *E); + Expr *VisitImplicitCastExpr(ImplicitCastExpr *E); }; } @@ -497,7 +501,7 @@ bool ASTNodeImporter::ImportDeclParts(NamedDecl *D, DeclContext *&DC, return false; } -bool ASTNodeImporter::ImportDeclParts(DeclaratorDecl *D, +bool ASTNodeImporter::ImportDeclParts(ValueDecl *D, DeclContext *&DC, DeclContext *&LexicalDC, DeclarationName &Name, @@ -518,7 +522,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord) { if (FromRecord->isUnion() != ToRecord->isUnion()) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.FromDiag(FromRecord->getLocation(), diag::note_odr_tag_kind_here) << FromRecord->getDeclName() << (unsigned)FromRecord->getTagKind(); @@ -529,7 +533,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, if (CXXRecordDecl *ToCXX = dyn_cast(ToRecord)) { if (FromCXX->getNumBases() != ToCXX->getNumBases()) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.ToDiag(ToRecord->getLocation(), diag::note_odr_number_of_bases) << ToCXX->getNumBases(); @@ -553,7 +557,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, if (!Importer.getToContext().typesAreCompatible(FromBaseT, ToBase->getType())) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.ToDiag(ToBase->getSourceRange().getBegin(), diag::note_odr_base) @@ -569,7 +573,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, // Check virtual vs. non-virtual inheritance mismatch. if (FromBase->isVirtual() != ToBase->isVirtual()) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.ToDiag(ToBase->getSourceRange().getBegin(), diag::note_odr_virtual_base) @@ -583,7 +587,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, } } else if (FromCXX->getNumBases() > 0) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); const CXXBaseSpecifier *FromBase = FromCXX->bases_begin(); Importer.FromDiag(FromBase->getSourceRange().getBegin(), @@ -604,7 +608,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, ++FromField, ++ToField) { if (ToField == ToFieldEnd) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.FromDiag(FromField->getLocation(), diag::note_odr_field) << FromField->getDeclName() << FromField->getType(); @@ -618,7 +622,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, if (!Importer.getToContext().typesAreCompatible(FromT, ToField->getType())){ Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.ToDiag(ToField->getLocation(), diag::note_odr_field) << ToField->getDeclName() << ToField->getType(); @@ -629,7 +633,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, if (FromField->isBitField() != ToField->isBitField()) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); if (FromField->isBitField()) { llvm::APSInt Bits; @@ -674,7 +678,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, if (FromBits != ToBits) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.ToDiag(ToField->getLocation(), diag::note_odr_bit_field) << ToField->getDeclName() << ToField->getType() @@ -689,7 +693,7 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, if (ToField != ToFieldEnd) { Importer.ToDiag(ToRecord->getLocation(), - diag::warn_odr_class_type_inconsistent) + diag::warn_odr_tag_type_inconsistent) << Importer.getToContext().getTypeDeclType(ToRecord); Importer.ToDiag(ToField->getLocation(), diag::note_odr_field) << ToField->getDeclName() << ToField->getType(); @@ -700,6 +704,67 @@ bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, return true; } +bool ASTNodeImporter::IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToEnum) { + EnumDecl::enumerator_iterator ToEC = ToEnum->enumerator_begin(), + ToEnd = ToEnum->enumerator_end(); + for (EnumDecl::enumerator_iterator FromEC = FromEnum->enumerator_begin(), + FromEnd = FromEnum->enumerator_end(); + FromEC != FromEnd; ++FromEC, ++ToEC) { + if (ToEC == ToEnd) { + Importer.ToDiag(ToEnum->getLocation(), + diag::warn_odr_tag_type_inconsistent) + << Importer.getToContext().getTypeDeclType(ToEnum); + Importer.FromDiag(FromEC->getLocation(), diag::note_odr_enumerator) + << FromEC->getDeclName() + << FromEC->getInitVal().toString(10); + Importer.ToDiag(ToEnum->getLocation(), + diag::note_odr_missing_enumerator); + return false; + } + + llvm::APSInt FromVal = FromEC->getInitVal(); + llvm::APSInt ToVal = ToEC->getInitVal(); + if (FromVal.getBitWidth() > ToVal.getBitWidth()) + ToVal.extend(FromVal.getBitWidth()); + else if (ToVal.getBitWidth() > FromVal.getBitWidth()) + FromVal.extend(ToVal.getBitWidth()); + if (FromVal.isSigned() != ToVal.isSigned()) { + if (FromVal.isSigned()) + ToVal.setIsSigned(true); + else + FromVal.setIsSigned(true); + } + + if (FromVal != ToVal || + ToEC->getDeclName() != Importer.Import(FromEC->getDeclName())) { + Importer.ToDiag(ToEnum->getLocation(), + diag::warn_odr_tag_type_inconsistent) + << Importer.getToContext().getTypeDeclType(ToEnum); + Importer.ToDiag(ToEC->getLocation(), diag::note_odr_enumerator) + << ToEC->getDeclName() + << ToEC->getInitVal().toString(10); + Importer.FromDiag(FromEC->getLocation(), diag::note_odr_enumerator) + << FromEC->getDeclName() + << FromEC->getInitVal().toString(10); + return false; + } + } + + if (ToEC != ToEnd) { + Importer.ToDiag(ToEnum->getLocation(), + diag::warn_odr_tag_type_inconsistent) + << Importer.getToContext().getTypeDeclType(ToEnum); + Importer.ToDiag(ToEC->getLocation(), diag::note_odr_enumerator) + << ToEC->getDeclName() + << ToEC->getInitVal().toString(10); + Importer.FromDiag(FromEnum->getLocation(), + diag::note_odr_missing_enumerator); + return false; + } + + return true; +} + Decl *ASTNodeImporter::VisitDecl(Decl *D) { Importer.FromDiag(D->getLocation(), diag::err_unsupported_ast_node) << D->getDeclKindName(); @@ -761,6 +826,94 @@ Decl *ASTNodeImporter::VisitTypedefDecl(TypedefDecl *D) { return ToTypedef; } +Decl *ASTNodeImporter::VisitEnumDecl(EnumDecl *D) { + // Import the major distinguishing characteristics of this enum. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + // Figure out what enum name we're looking for. + unsigned IDNS = Decl::IDNS_Tag; + DeclarationName SearchName = Name; + if (!SearchName && D->getTypedefForAnonDecl()) { + SearchName = Importer.Import(D->getTypedefForAnonDecl()->getDeclName()); + IDNS = Decl::IDNS_Ordinary; + } else if (Importer.getToContext().getLangOptions().CPlusPlus) + IDNS |= Decl::IDNS_Ordinary; + + // We may already have an enum of the same name; try to find and match it. + if (!DC->isFunctionOrMethod() && SearchName) { + llvm::SmallVector ConflictingDecls; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + Decl *Found = *Lookup.first; + if (TypedefDecl *Typedef = dyn_cast(Found)) { + if (const TagType *Tag = Typedef->getUnderlyingType()->getAs()) + Found = Tag->getDecl(); + } + + if (EnumDecl *FoundEnum = dyn_cast(Found)) { + if (IsStructuralMatch(D, FoundEnum)) { + // The enum types structurally match. + Importer.getImportedDecls()[D] = FoundEnum; + return FoundEnum; + } + } + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + } + } + + // Create the enum declaration. + EnumDecl *ToEnum = EnumDecl::Create(Importer.getToContext(), DC, Loc, + Name.getAsIdentifierInfo(), + Importer.Import(D->getTagKeywordLoc()), + 0); + ToEnum->setLexicalDeclContext(LexicalDC); + Importer.getImportedDecls()[D] = ToEnum; + LexicalDC->addDecl(ToEnum); + + // Import the integer type. + QualType ToIntegerType = Importer.Import(D->getIntegerType()); + if (ToIntegerType.isNull()) + return 0; + ToEnum->setIntegerType(ToIntegerType); + + // Import the definition + if (D->isDefinition()) { + QualType T = Importer.Import(Importer.getFromContext().getTypeDeclType(D)); + if (T.isNull()) + return 0; + + QualType ToPromotionType = Importer.Import(D->getPromotionType()); + if (ToPromotionType.isNull()) + return 0; + + ToEnum->startDefinition(); + for (DeclContext::decl_iterator FromMem = D->decls_begin(), + FromMemEnd = D->decls_end(); + FromMem != FromMemEnd; + ++FromMem) + Importer.Import(*FromMem); + + ToEnum->completeDefinition(T, ToPromotionType); + } + + return ToEnum; +} + Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *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 @@ -891,6 +1044,51 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { return ToRecord; } +Decl *ASTNodeImporter::VisitEnumConstantDecl(EnumConstantDecl *D) { + // Import the major distinguishing characteristics of this enumerator. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + QualType T; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc, T)) + return 0; + + // Determine whether there are any other declarations with the same name and + // in the same context. + if (!LexicalDC->isFunctionOrMethod()) { + llvm::SmallVector ConflictingDecls; + unsigned IDNS = Decl::IDNS_Ordinary; + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (!(*Lookup.first)->isInIdentifierNamespace(IDNS)) + continue; + + ConflictingDecls.push_back(*Lookup.first); + } + + if (!ConflictingDecls.empty()) { + Name = Importer.HandleNameConflict(Name, DC, IDNS, + ConflictingDecls.data(), + ConflictingDecls.size()); + if (!Name) + return 0; + } + } + + Expr *Init = Importer.Import(D->getInitExpr()); + if (D->getInitExpr() && !Init) + return 0; + + EnumConstantDecl *ToEnumerator + = EnumConstantDecl::Create(Importer.getToContext(), cast(DC), Loc, + Name.getAsIdentifierInfo(), T, + Init, D->getInitVal()); + ToEnumerator->setLexicalDeclContext(LexicalDC); + Importer.getImportedDecls()[D] = ToEnumerator; + LexicalDC->addDecl(ToEnumerator); + return ToEnumerator; +} Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // Import the major distinguishing characteristics of this function. @@ -963,25 +1161,25 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // Create the imported function. TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); - FunctionDecl *ToFunction + FunctionDecl *ToEnumerator = FunctionDecl::Create(Importer.getToContext(), DC, Loc, Name, T, TInfo, D->getStorageClass(), D->isInlineSpecified(), D->hasWrittenPrototype()); - ToFunction->setLexicalDeclContext(LexicalDC); - Importer.getImportedDecls()[D] = ToFunction; - LexicalDC->addDecl(ToFunction); + ToEnumerator->setLexicalDeclContext(LexicalDC); + Importer.getImportedDecls()[D] = ToEnumerator; + LexicalDC->addDecl(ToEnumerator); // Set the parameters. for (unsigned I = 0, N = Parameters.size(); I != N; ++I) { - Parameters[I]->setOwningFunction(ToFunction); - ToFunction->addDecl(Parameters[I]); + Parameters[I]->setOwningFunction(ToEnumerator); + ToEnumerator->addDecl(Parameters[I]); } - ToFunction->setParams(Parameters.data(), Parameters.size()); + ToEnumerator->setParams(Parameters.data(), Parameters.size()); // FIXME: Other bits to merge? - return ToFunction; + return ToEnumerator; } Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { @@ -1170,6 +1368,20 @@ Expr *ASTNodeImporter::VisitIntegerLiteral(IntegerLiteral *E) { IntegerLiteral(E->getValue(), T, Importer.Import(E->getLocation())); } +Expr *ASTNodeImporter::VisitImplicitCastExpr(ImplicitCastExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return 0; + + Expr *SubExpr = Importer.Import(E->getSubExpr()); + if (!SubExpr) + return 0; + + return new (Importer.getToContext()) ImplicitCastExpr(T, E->getCastKind(), + SubExpr, + E->isLvalueCast()); +} + ASTImporter::ASTImporter(Diagnostic &Diags, ASTContext &ToContext, FileManager &ToFileManager, ASTContext &FromContext, FileManager &FromFileManager) diff --git a/test/ASTMerge/Inputs/enum1.c b/test/ASTMerge/Inputs/enum1.c new file mode 100644 index 0000000000..cbbed47bac --- /dev/null +++ b/test/ASTMerge/Inputs/enum1.c @@ -0,0 +1,34 @@ +// Matching +enum E1 { + E1Enumerator1, + E1Enumerator2 = 3, + E1Enumerator3 +} x1; + +// Value mismatch +enum E2 { + E2Enumerator1, + E2Enumerator2 = 3, + E2Enumerator3 +} x2; + +// Name mismatch +enum E3 { + E3Enumerator1, + E3Enumerator2 = 3, + E3Enumerator3 +} x3; + +// Missing enumerator +enum E4 { + E4Enumerator1, + E4Enumerator2, + E4Enumerator3 +} x4; + +// Extra enumerator +enum E5 { + E5Enumerator1, + E5Enumerator2, + E5Enumerator3 +} x5; diff --git a/test/ASTMerge/Inputs/enum2.c b/test/ASTMerge/Inputs/enum2.c new file mode 100644 index 0000000000..050af828fd --- /dev/null +++ b/test/ASTMerge/Inputs/enum2.c @@ -0,0 +1,35 @@ +// Matching +enum E1 { + E1Enumerator1, + E1Enumerator2 = 3, + E1Enumerator3 +} x1; + +// Value mismatch +enum E2 { + E2Enumerator1, + E2Enumerator2 = 4, + E2Enumerator3 +} x2; + +// Name mismatch +enum E3 { + E3Enumerator1, + E3Enumerator = 3, + E3Enumerator3 +} x3; + +// Missing enumerator +enum E4 { + E4Enumerator1, + E4Enumerator2 +} x4; + +// Extra enumerator +enum E5 { + E5Enumerator1, + E5Enumerator2, + E5Enumerator3, + E5Enumerator4 +} x5; + diff --git a/test/ASTMerge/enum.c b/test/ASTMerge/enum.c new file mode 100644 index 0000000000..9b78fd1022 --- /dev/null +++ b/test/ASTMerge/enum.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/enum1.c +// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/enum2.c +// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: enum1.c:9:6: warning: type 'enum E2' has incompatible definitions in different translation units +// CHECK: enum1.c:11:3: note: enumerator 'E2Enumerator2' with value 3 here +// CHECK: enum2.c:11:3: note: enumerator 'E2Enumerator2' with value 4 here +// CHECK: enum2.c:13:3: error: external variable 'x2' declared with incompatible types in different translation units ('enum E2' vs. 'enum E2') +// CHECK: enum1.c:13:3: note: declared here with type 'enum E2' +// CHECK: enum1.c:16:6: warning: type 'enum E3' has incompatible definitions in different translation units +// CHECK: enum1.c:18:3: note: enumerator 'E3Enumerator2' with value 3 here +// CHECK: enum2.c:18:3: note: enumerator 'E3Enumerator' with value 3 here +// CHECK: enum2.c:20:3: error: external variable 'x3' declared with incompatible types in different translation units ('enum E3' vs. 'enum E3') +// CHECK: enum1.c:20:3: note: declared here with type 'enum E3' +// CHECK: enum1.c:23:6: warning: type 'enum E4' has incompatible definitions in different translation units +// CHECK: enum1.c:26:3: note: enumerator 'E4Enumerator3' with value 2 here +// CHECK: enum2.c:23:6: note: no corresponding enumerator here +// CHECK: enum2.c:26:3: error: external variable 'x4' declared with incompatible types in different translation units ('enum E4' vs. 'enum E4') +// CHECK: enum1.c:27:3: note: declared here with type 'enum E4' +// CHECK: enum1.c:30:6: warning: type 'enum E5' has incompatible definitions in different translation units +// CHECK: enum2.c:33:3: note: enumerator 'E5Enumerator4' with value 3 here +// CHECK: enum1.c:30:6: note: no corresponding enumerator here +// CHECK: enum2.c:34:3: error: external variable 'x5' declared with incompatible types in different translation units ('enum E5' vs. 'enum E5') +// CHECK: enum1.c:34:3: note: declared here with type 'enum E5' +// CHECK: 20 diagnostics generated