[clang] d32c685 - [modules] Merge equivalent extensions and diagnose ivar redeclarations for extensions loaded from different modules.
Volodymyr Sapsai via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 27 15:53:11 PDT 2022
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
+ at interface NSObject @end
+ at interface ObjCInterface : NSObject
+ at end
+
+//--- Frameworks/InterfaceAndExtension.framework/Headers/Extension.h
+#import <InterfaceAndExtension/Interface.h>
+ at interface ObjCInterface() {
+ float ivarInExtension;
+ int bitfieldIvarInExtension: 3;
+}
+ at 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>
+ at implementation ObjCInterface {
+ int ivarInImplementation;
+}
+ at 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>
+ at interface ObjCInterface() {
+ at public
+ int accessedIvar;
+}
+ at 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>
+ at interface ObjCInterface() {
+ at public
+ int accessedIvar;
+}
+ at end
+#import <WithInlineIvar/WithInlineIvar.h>
+ at implementation ObjCInterface
+- (void)test {
+ inlinedIvarAccessor(self);
+ ivarInExtension = 0;
+}
+ at 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
+ at interface NSObject @end
+ at interface WithProperty: NSObject
+ at property (assign) int propertyName;
+ at end
+
+//--- Frameworks/WithProperty.framework/Modules/module.modulemap
+framework module WithProperty {
+ header "WithProperty.h"
+ export *
+}
+
+//--- Frameworks/BackingIvarInExtension.framework/Headers/BackingIvarInExtension.h
+#import <WithProperty/WithProperty.h>
+ at interface WithProperty() {
+ int propertyBackingIvar;
+}
+ at 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
+ at implementation WithProperty
+ at synthesize propertyName = propertyBackingIvar;
+ at 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
+ at interface NSObject @end
+ at interface ObjCInterface : NSObject
+ at end
+ at interface ObjCInterfaceLevel2 : ObjCInterface
+ at end
+
+ at protocol Protocol1 @end
+ at protocol Protocol2 @end
+
+//--- include/IvarsInExtensions.h
+#import <Interfaces.h>
+ at interface ObjCInterface() {
+ int ivarName;
+}
+ at end
+ at interface ObjCInterfaceLevel2() {
+ int bitfieldIvarName: 3;
+}
+ at end
+
+//--- include/IvarsInExtensionsWithMethodsProtocols.h
+#import <Interfaces.h>
+ at interface ObjCInterface() {
+ int methodRelatedIvar;
+}
+- (void)test;
+ at end
+ at interface ObjCInterfaceLevel2() <Protocol1> {
+ int protocolRelatedIvar;
+}
+ at end
+
+//--- include/IvarInImplementation.h
+#import <Interfaces.h>
+ at implementation ObjCInterface {
+ int ivarName;
+}
+ at 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>
+ at interface ObjCInterface() {
+ float ivarName; // expected-note {{previous definition is here}}
+}
+ at end
+ at interface ObjCInterfaceLevel2() {
+ int bitfieldIvarName: 5; // expected-note {{previous definition is here}}
+}
+ at end
+#import <IvarsInExtensions.h>
+// expected-error at IvarsInExtensions.h:* {{instance variable is already declared}}
+// expected-error at IvarsInExtensions.h:* {{instance variable is already declared}}
+ at implementation ObjCInterfaceLevel2
+ at end
+
+
+//--- test-mismatch-in-ivars-number.m
+// Extensions with
diff erent amount of ivars aren't considered to be the same.
+#import <Interfaces.h>
+ at interface ObjCInterface() {
+ int ivarName; // expected-note {{previous definition is here}}
+ float anotherIvar;
+}
+ at end
+#import <IvarsInExtensions.h>
+// expected-error at IvarsInExtensions.h:* {{instance variable is already declared}}
+ at implementation ObjCInterface
+ at end
+
+
+//--- test-mismatch-in-methods-protocols.m
+// Extensions with
diff erent methods or protocols aren't considered to be the same.
+#import <Interfaces.h>
+ at interface ObjCInterface() {
+ int methodRelatedIvar; // expected-note {{previous definition is here}}
+}
+- (void)
diff erentTest;
+ at end
+ at interface ObjCInterfaceLevel2() <Protocol2> {
+ int protocolRelatedIvar; // expected-note {{previous definition is here}}
+}
+ at end
+#import <IvarsInExtensionsWithMethodsProtocols.h>
+// expected-error at IvarsInExtensionsWithMethodsProtocols.h:* {{instance variable is already declared}}
+// expected-error at IvarsInExtensionsWithMethodsProtocols.h:* {{instance variable is already declared}}
+ at implementation ObjCInterfaceLevel2
+ at 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>
+ at interface ObjCInterfaceLevel2() {
+ float ivarName;
+}
+ at end
+#import <IvarsInExtensions.h>
+ at implementation ObjCInterfaceLevel2
+ at end
+
+
+//--- test-redecl-in-implementation.m
+// Ivar redeclaration in `@implementation` is always an error and never mergeable.
+#import <IvarsInExtensions.h>
+ at interface ObjCInterface() {
+ int triggerExtensionIvarDeserialization;
+}
+ at end
+#import <IvarInImplementation.h>
+#if __has_feature(modules)
+// expected-error at IvarsInExtensions.h:* {{instance variable is already declared}}
+// expected-note at IvarInImplementation.h:* {{previous definition is here}}
+#else
+// expected-error at IvarInImplementation.h:* {{instance variable is already declared}}
+// expected-note at IvarsInExtensions.h:* {{previous definition is here}}
+#endif
More information about the cfe-commits
mailing list