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

Reply via email to