Author: Volodymyr Sapsai Date: 2022-04-27T15:52:59-07:00 New Revision: d32c685e1012785c0d2824214740f973d30e1daa
URL: https://github.com/llvm/llvm-project/commit/d32c685e1012785c0d2824214740f973d30e1daa DIFF: https://github.com/llvm/llvm-project/commit/d32c685e1012785c0d2824214740f973d30e1daa.diff LOG: [modules] Merge equivalent extensions and diagnose ivar redeclarations for extensions loaded from different modules. Emitting metadata for the same ivar multiple times can lead to miscompilations. Objective-C runtime adds offsets to calculate ivar position in memory and presence of duplicate offsets causes wrong final position thus overwriting unrelated memory. Such a situation is impossible with modules disabled as clang diagnoses ivar redeclarations during sema checks after parsing (`Sema::ActOnFields`). Fix the case with modules enabled by checking during deserialization if ivar is already declared. We also support a use case where the same category ends up in multiple modules. We don't want to treat this case as ivar redeclaration and instead merge corresponding ivars. rdar://83468070 Differential Revision: https://reviews.llvm.org/D121177 Added: clang/test/Modules/merge-extension-ivars.m clang/test/Modules/redecl-ivars.m Modified: clang/include/clang/AST/DeclObjC.h clang/include/clang/Serialization/ASTReader.h clang/lib/AST/DeclObjC.cpp clang/lib/Serialization/ASTReader.cpp clang/lib/Serialization/ASTReaderDecl.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 110b7dc0c6f2..59a2cf5de234 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -1951,7 +1951,10 @@ class ObjCIvarDecl : public FieldDecl { /// in; this is either the interface where the ivar was declared, or the /// interface the ivar is conceptually a part of in the case of synthesized /// ivars. - const ObjCInterfaceDecl *getContainingInterface() const; + ObjCInterfaceDecl *getContainingInterface(); + const ObjCInterfaceDecl *getContainingInterface() const { + return const_cast<ObjCIvarDecl *>(this)->getContainingInterface(); + } ObjCIvarDecl *getNextIvar() { return NextIvar; } const ObjCIvarDecl *getNextIvar() const { return NextIvar; } @@ -2885,15 +2888,16 @@ ObjCInterfaceDecl::filtered_category_iterator<Filter>::operator++() { } inline bool ObjCInterfaceDecl::isVisibleCategory(ObjCCategoryDecl *Cat) { - return Cat->isUnconditionallyVisible(); + return !Cat->isInvalidDecl() && Cat->isUnconditionallyVisible(); } inline bool ObjCInterfaceDecl::isVisibleExtension(ObjCCategoryDecl *Cat) { - return Cat->IsClassExtension() && Cat->isUnconditionallyVisible(); + return !Cat->isInvalidDecl() && Cat->IsClassExtension() && + Cat->isUnconditionallyVisible(); } inline bool ObjCInterfaceDecl::isKnownExtension(ObjCCategoryDecl *Cat) { - return Cat->IsClassExtension(); + return !Cat->isInvalidDecl() && Cat->IsClassExtension(); } } // namespace clang diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index d46a6c4500f4..8e8e40a5cd37 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1106,6 +1106,18 @@ class ASTReader /// been completed. std::deque<PendingDeclContextInfo> PendingDeclContextInfos; + template <typename DeclTy> + using DuplicateObjCDecls = std::pair<DeclTy *, DeclTy *>; + + /// When resolving duplicate ivars from Objective-C extensions we don't error + /// out immediately but check if can merge identical extensions. Not checking + /// extensions for equality immediately because ivar deserialization isn't + /// over yet at that point. + llvm::SmallMapVector<DuplicateObjCDecls<ObjCCategoryDecl>, + llvm::SmallVector<DuplicateObjCDecls<ObjCIvarDecl>, 4>, + 2> + PendingObjCExtensionIvarRedeclarations; + /// The set of NamedDecls that have been loaded, but are members of a /// context that has been merged into another context where the corresponding /// declaration is either missing or has not yet been loaded. diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index f15dd78929e2..15c545b59c81 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1647,6 +1647,11 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { ObjCIvarDecl *curIvar = nullptr; if (!data().IvarList) { + // Force ivar deserialization upfront, before building IvarList. + (void)ivar_empty(); + for (const auto *Ext : known_extensions()) { + (void)Ext->ivar_empty(); + } if (!ivar_empty()) { ObjCInterfaceDecl::ivar_iterator I = ivar_begin(), E = ivar_end(); data().IvarList = *I; ++I; @@ -1838,8 +1843,8 @@ ObjCIvarDecl *ObjCIvarDecl::CreateDeserialized(ASTContext &C, unsigned ID) { ObjCIvarDecl::None, nullptr, false); } -const ObjCInterfaceDecl *ObjCIvarDecl::getContainingInterface() const { - const auto *DC = cast<ObjCContainerDecl>(getDeclContext()); +ObjCInterfaceDecl *ObjCIvarDecl::getContainingInterface() { + auto *DC = cast<ObjCContainerDecl>(getDeclContext()); switch (DC->getKind()) { default: @@ -1849,7 +1854,7 @@ const ObjCInterfaceDecl *ObjCIvarDecl::getContainingInterface() const { // Ivars can only appear in class extension categories. case ObjCCategory: { - const auto *CD = cast<ObjCCategoryDecl>(DC); + auto *CD = cast<ObjCCategoryDecl>(DC); assert(CD->IsClassExtension() && "invalid container for ivar!"); return CD->getClassInterface(); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 74cc5c73c803..c9601f0a164c 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -15,6 +15,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/ASTUnresolvedSet.h" #include "clang/AST/AbstractTypeReader.h" #include "clang/AST/Decl.h" @@ -42,6 +43,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticError.h" #include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/ExceptionSpecificationType.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" @@ -9193,7 +9195,8 @@ void ASTReader::finishPendingActions() { while (!PendingIdentifierInfos.empty() || !PendingFunctionTypes.empty() || !PendingIncompleteDeclChains.empty() || !PendingDeclChains.empty() || !PendingMacroIDs.empty() || !PendingDeclContextInfos.empty() || - !PendingUpdateRecords.empty()) { + !PendingUpdateRecords.empty() || + !PendingObjCExtensionIvarRedeclarations.empty()) { // If any identifiers with corresponding top-level declarations have // been loaded, load those declarations now. using TopLevelDeclsMap = @@ -9284,6 +9287,43 @@ void ASTReader::finishPendingActions() { ReadingKindTracker ReadingKind(Read_Decl, *this); loadDeclUpdateRecords(Update); } + + while (!PendingObjCExtensionIvarRedeclarations.empty()) { + auto ExtensionsPair = PendingObjCExtensionIvarRedeclarations.back().first; + auto DuplicateIvars = + PendingObjCExtensionIvarRedeclarations.back().second; + llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls; + StructuralEquivalenceContext Ctx( + ExtensionsPair.first->getASTContext(), + ExtensionsPair.second->getASTContext(), NonEquivalentDecls, + StructuralEquivalenceKind::Default, /*StrictTypeSpelling =*/false, + /*Complain =*/false, + /*ErrorOnTagTypeMismatch =*/true); + if (Ctx.IsEquivalent(ExtensionsPair.first, ExtensionsPair.second)) { + // Merge redeclared ivars with their predecessors. + for (auto IvarPair : DuplicateIvars) { + ObjCIvarDecl *Ivar = IvarPair.first, *PrevIvar = IvarPair.second; + // Change semantic DeclContext but keep the lexical one. + Ivar->setDeclContextsImpl(PrevIvar->getDeclContext(), + Ivar->getLexicalDeclContext(), + getContext()); + getContext().setPrimaryMergedDecl(Ivar, PrevIvar->getCanonicalDecl()); + } + // Invalidate duplicate extension and the cached ivar list. + ExtensionsPair.first->setInvalidDecl(); + ExtensionsPair.second->getClassInterface() + ->getDefinition() + ->setIvarList(nullptr); + } else { + for (auto IvarPair : DuplicateIvars) { + Diag(IvarPair.first->getLocation(), + diag::err_duplicate_ivar_declaration) + << IvarPair.first->getIdentifier(); + Diag(IvarPair.second->getLocation(), diag::note_previous_definition); + } + } + PendingObjCExtensionIvarRedeclarations.pop_back(); + } } // At this point, all update records for loaded decls are in place, so any diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9e0c3d558323..86a9c7733069 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -36,6 +36,7 @@ #include "clang/AST/Type.h" #include "clang/AST/UnresolvedSet.h" #include "clang/Basic/AttrKinds.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/ExceptionSpecificationType.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" @@ -1232,6 +1233,39 @@ void ASTDeclReader::VisitObjCIvarDecl(ObjCIvarDecl *IVD) { IVD->setNextIvar(nullptr); bool synth = Record.readInt(); IVD->setSynthesize(synth); + + // Check ivar redeclaration. + if (IVD->isInvalidDecl()) + return; + // Don't check ObjCInterfaceDecl as interfaces are named and mismatches can be + // detected in VisitObjCInterfaceDecl. Here we are looking for redeclarations + // in extensions. + if (isa<ObjCInterfaceDecl>(IVD->getDeclContext())) + return; + ObjCInterfaceDecl *CanonIntf = + IVD->getContainingInterface()->getCanonicalDecl(); + IdentifierInfo *II = IVD->getIdentifier(); + ObjCIvarDecl *PrevIvar = CanonIntf->lookupInstanceVariable(II); + if (PrevIvar && PrevIvar != IVD) { + auto *ParentExt = dyn_cast<ObjCCategoryDecl>(IVD->getDeclContext()); + auto *PrevParentExt = + dyn_cast<ObjCCategoryDecl>(PrevIvar->getDeclContext()); + if (ParentExt && PrevParentExt) { + // Postpone diagnostic as we should merge identical extensions from + // diff erent modules. + Reader + .PendingObjCExtensionIvarRedeclarations[std::make_pair(ParentExt, + PrevParentExt)] + .push_back(std::make_pair(IVD, PrevIvar)); + } else if (ParentExt || PrevParentExt) { + // Duplicate ivars in extension + implementation are never compatible. + // Compatibility of implementation + implementation should be handled in + // VisitObjCImplementationDecl. + Reader.Diag(IVD->getLocation(), diag::err_duplicate_ivar_declaration) + << II; + Reader.Diag(PrevIvar->getLocation(), diag::note_previous_definition); + } + } } void ASTDeclReader::ReadObjCDefinitionData( diff --git a/clang/test/Modules/merge-extension-ivars.m b/clang/test/Modules/merge-extension-ivars.m new file mode 100644 index 000000000000..8dead2c0390c --- /dev/null +++ b/clang/test/Modules/merge-extension-ivars.m @@ -0,0 +1,146 @@ +// UNSUPPORTED: -zos, -aix +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -emit-llvm -o %t/test-compatible-extensions.ll -fobjc-runtime=macosx-10.9 -F%t/Frameworks %t/test-compatible-extensions.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache -fmodule-name=InterfaceAndExtension +// RUN: FileCheck --input-file=%t/test-compatible-extensions.ll %t/test-compatible-extensions.m + +// RUN: %clang_cc1 -emit-llvm -o %t/test-access-extension-ivar.ll -fobjc-runtime=macosx-10.9 -F%t/Frameworks %t/test-access-extension-ivar.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache +// RUN: FileCheck --input-file=%t/test-access-extension-ivar.ll %t/test-access-extension-ivar.m + +// RUN: %clang_cc1 -emit-llvm -o %t/test-synthesized-ivar.ll -fobjc-runtime=macosx-10.9 -F%t/Frameworks %t/test-synthesized-ivar.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache +// RUN: FileCheck --input-file=%t/test-synthesized-ivar.ll %t/test-synthesized-ivar.m +// RUN: %clang_cc1 -emit-llvm -o %t/test-synthesized-ivar-extension.ll -fobjc-runtime=macosx-10.9 -F%t/Frameworks %t/test-synthesized-ivar.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache \ +// RUN: -DIMPORT_EXTENSION=1 +// RUN: FileCheck --input-file=%t/test-synthesized-ivar-extension.ll %t/test-synthesized-ivar.m + +// Test various scenarios where we can end up with the same-name ivars coming from multiple modules. +// The goal is to avoid duplicate metadata for ivars because it can lead to miscompilations +// with a wrong ivar offset. +// +// See specific .m tests for the details of various scenarios. + +//--- Frameworks/InterfaceAndExtension.framework/Headers/Interface.h +@interface NSObject @end +@interface ObjCInterface : NSObject +@end + +//--- Frameworks/InterfaceAndExtension.framework/Headers/Extension.h +#import <InterfaceAndExtension/Interface.h> +@interface ObjCInterface() { + float ivarInExtension; + int bitfieldIvarInExtension: 3; +} +@end + +//--- Frameworks/InterfaceAndExtension.framework/Headers/InterfaceAndExtension.h +#import <InterfaceAndExtension/Interface.h> +#import <InterfaceAndExtension/Extension.h> + +//--- Frameworks/InterfaceAndExtension.framework/Modules/module.modulemap +framework module InterfaceAndExtension { + umbrella header "InterfaceAndExtension.h" + export * + module * { export * } +} + +//--- Frameworks/Redirecting.framework/Headers/Redirecting.h +#import <InterfaceAndExtension/InterfaceAndExtension.h> + +//--- Frameworks/Redirecting.framework/Modules/module.modulemap +framework module Redirecting { + header "Redirecting.h" + export * +} + +//--- test-compatible-extensions.m +// Test adding through deserialization an extension with already declared ivars. + +// First create `ObjCInterface()` extension by parsing corresponding code. +#import <InterfaceAndExtension/InterfaceAndExtension.h> +// Now add the same extension through deserialization from the imported module. +#import <Redirecting/Redirecting.h> +@implementation ObjCInterface { + int ivarInImplementation; +} +@end +// CHECK: @"_OBJC_$_INSTANCE_VARIABLES_ObjCInterface" +// CHECK-SAME: [3 x %struct._ivar_t] [%struct._ivar_t { ptr @"OBJC_IVAR_$_ObjCInterface.ivarInExtension", {{.*}} }, %struct._ivar_t { ptr @"OBJC_IVAR_$_ObjCInterface.bitfieldIvarInExtension", {{.*}} }, %struct._ivar_t { ptr @"OBJC_IVAR_$_ObjCInterface.ivarInImplementation", {{.*}} }] + + +//--- Frameworks/WithInlineIvar.framework/Headers/WithInlineIvar.h +#import <InterfaceAndExtension/InterfaceAndExtension.h> +@interface ObjCInterface() { +@public + int accessedIvar; +} +@end +static inline void inlinedIvarAccessor(ObjCInterface *obj) { + obj->accessedIvar = 0; +} + +//--- Frameworks/WithInlineIvar.framework/Modules/module.modulemap +framework module WithInlineIvar { + header "WithInlineIvar.h" + export * +} + +//--- test-access-extension-ivar.m +// Test accessing ivars from extensions. +#import <InterfaceAndExtension/InterfaceAndExtension.h> +@interface ObjCInterface() { +@public + int accessedIvar; +} +@end +#import <WithInlineIvar/WithInlineIvar.h> +@implementation ObjCInterface +- (void)test { + inlinedIvarAccessor(self); + ivarInExtension = 0; +} +@end +// CHECK: @"_OBJC_$_INSTANCE_VARIABLES_ObjCInterface" +// CHECK-SAME: [3 x %struct._ivar_t] [%struct._ivar_t { ptr @"OBJC_IVAR_$_ObjCInterface.accessedIvar", {{.*}} }, %struct._ivar_t { ptr @"OBJC_IVAR_$_ObjCInterface.ivarInExtension", {{.*}} }, %struct._ivar_t { ptr @"OBJC_IVAR_$_ObjCInterface.bitfieldIvarInExtension", {{.*}} }] + + +//--- Frameworks/WithProperty.framework/Headers/WithProperty.h +@interface NSObject @end +@interface WithProperty: NSObject +@property (assign) int propertyName; +@end + +//--- Frameworks/WithProperty.framework/Modules/module.modulemap +framework module WithProperty { + header "WithProperty.h" + export * +} + +//--- Frameworks/BackingIvarInExtension.framework/Headers/BackingIvarInExtension.h +#import <WithProperty/WithProperty.h> +@interface WithProperty() { + int propertyBackingIvar; +} +@end + +//--- Frameworks/BackingIvarInExtension.framework/Modules/module.modulemap +framework module BackingIvarInExtension { + header "BackingIvarInExtension.h" + export * +} + +//--- test-synthesized-ivar.m +// Test when an ivar is both synthesized and when declared in an extension. +// Behavior with and without extension should be the same. +#import <WithProperty/WithProperty.h> +#ifdef IMPORT_EXTENSION +#import <BackingIvarInExtension/BackingIvarInExtension.h> +#endif +@implementation WithProperty +@synthesize propertyName = propertyBackingIvar; +@end +// CHECK: @"_OBJC_$_INSTANCE_VARIABLES_WithProperty" +// CHECK-SAME: [1 x %struct._ivar_t] [%struct._ivar_t { ptr @"OBJC_IVAR_$_WithProperty.propertyBackingIvar", {{.*}} }] diff --git a/clang/test/Modules/redecl-ivars.m b/clang/test/Modules/redecl-ivars.m new file mode 100644 index 000000000000..050b7520ab3b --- /dev/null +++ b/clang/test/Modules/redecl-ivars.m @@ -0,0 +1,166 @@ +// UNSUPPORTED: -zos, -aix +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-mismatch-in-extension.m +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-mismatch-in-extension.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache + +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-mismatch-in-ivars-number.m +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-mismatch-in-ivars-number.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache + +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-mismatch-in-methods-protocols.m +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-mismatch-in-methods-protocols.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache + +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-redecl-in-subclass.m +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-redecl-in-subclass.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache + +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-redecl-in-implementation.m +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime=macosx-10.9 -verify -I%t/include %t/test-redecl-in-implementation.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache + +// Same class extensions with the same ivars but from diff erent modules aren't considered +// an error and they are merged together. Test that diff erences in extensions and/or ivars +// are still reported as errors. + +//--- include/Interfaces.h +@interface NSObject @end +@interface ObjCInterface : NSObject +@end +@interface ObjCInterfaceLevel2 : ObjCInterface +@end + +@protocol Protocol1 @end +@protocol Protocol2 @end + +//--- include/IvarsInExtensions.h +#import <Interfaces.h> +@interface ObjCInterface() { + int ivarName; +} +@end +@interface ObjCInterfaceLevel2() { + int bitfieldIvarName: 3; +} +@end + +//--- include/IvarsInExtensionsWithMethodsProtocols.h +#import <Interfaces.h> +@interface ObjCInterface() { + int methodRelatedIvar; +} +- (void)test; +@end +@interface ObjCInterfaceLevel2() <Protocol1> { + int protocolRelatedIvar; +} +@end + +//--- include/IvarInImplementation.h +#import <Interfaces.h> +@implementation ObjCInterface { + int ivarName; +} +@end + +//--- include/module.modulemap +module Interfaces { + header "Interfaces.h" + export * +} +module IvarsInExtensions { + header "IvarsInExtensions.h" + export * +} +module IvarsInExtensionsWithMethodsProtocols { + header "IvarsInExtensionsWithMethodsProtocols.h" + export * +} +module IvarInImplementation { + header "IvarInImplementation.h" + export * +} + + +//--- test-mismatch-in-extension.m +// Different ivars with the same name aren't mergeable and constitute an error. +#import <Interfaces.h> +@interface ObjCInterface() { + float ivarName; // expected-note {{previous definition is here}} +} +@end +@interface ObjCInterfaceLevel2() { + int bitfieldIvarName: 5; // expected-note {{previous definition is here}} +} +@end +#import <IvarsInExtensions.h> +// expected-error@IvarsInExtensions.h:* {{instance variable is already declared}} +// expected-error@IvarsInExtensions.h:* {{instance variable is already declared}} +@implementation ObjCInterfaceLevel2 +@end + + +//--- test-mismatch-in-ivars-number.m +// Extensions with diff erent amount of ivars aren't considered to be the same. +#import <Interfaces.h> +@interface ObjCInterface() { + int ivarName; // expected-note {{previous definition is here}} + float anotherIvar; +} +@end +#import <IvarsInExtensions.h> +// expected-error@IvarsInExtensions.h:* {{instance variable is already declared}} +@implementation ObjCInterface +@end + + +//--- test-mismatch-in-methods-protocols.m +// Extensions with diff erent methods or protocols aren't considered to be the same. +#import <Interfaces.h> +@interface ObjCInterface() { + int methodRelatedIvar; // expected-note {{previous definition is here}} +} +- (void) diff erentTest; +@end +@interface ObjCInterfaceLevel2() <Protocol2> { + int protocolRelatedIvar; // expected-note {{previous definition is here}} +} +@end +#import <IvarsInExtensionsWithMethodsProtocols.h> +// expected-error@IvarsInExtensionsWithMethodsProtocols.h:* {{instance variable is already declared}} +// expected-error@IvarsInExtensionsWithMethodsProtocols.h:* {{instance variable is already declared}} +@implementation ObjCInterfaceLevel2 +@end + + +//--- test-redecl-in-subclass.m +// Ivar in superclass extension is not added to a subclass, so the ivar with +// the same name in subclass extension is not considered a redeclaration. +// expected-no-diagnostics +#import <Interfaces.h> +@interface ObjCInterfaceLevel2() { + float ivarName; +} +@end +#import <IvarsInExtensions.h> +@implementation ObjCInterfaceLevel2 +@end + + +//--- test-redecl-in-implementation.m +// Ivar redeclaration in `@implementation` is always an error and never mergeable. +#import <IvarsInExtensions.h> +@interface ObjCInterface() { + int triggerExtensionIvarDeserialization; +} +@end +#import <IvarInImplementation.h> +#if __has_feature(modules) +// expected-error@IvarsInExtensions.h:* {{instance variable is already declared}} +// expected-note@IvarInImplementation.h:* {{previous definition is here}} +#else +// expected-error@IvarInImplementation.h:* {{instance variable is already declared}} +// expected-note@IvarsInExtensions.h:* {{previous definition is here}} +#endif _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits