Skip to content

Commit 2dff8fc

Browse files
committed
[modules] Merge ObjC interface ivars with anonymous types.
Without the fix ivars with anonymous types can trigger errors like > error: 'TestClass::structIvar' from module 'Target' is not present in definition of 'TestClass' provided earlier > [...] > note: declaration of 'structIvar' does not match It happens because types of ivars from different modules are considered to be different. And it is caused by not merging anonymous `TagDecl` from different modules. To fix that I've changed `serialization::needsAnonymousDeclarationNumber` to handle anonymous `TagDecl` inside `ObjCInterfaceDecl`. But that's not sufficient as C code inside `ObjCInterfaceDecl` doesn't use interface decl as a decl context but switches to its parent (TranslationUnit in most cases). I'm changing that to make `ObjCContainerDecl` the lexical decl context but keeping the semantic decl context intact. Test "check-dup-decls-inside-objc.m" doesn't reflect a change in functionality but captures the existing behavior to prevent regressions. rdar://85563013 Differential Revision: https://reviews.llvm.org/D118525 (cherry picked from commit 29444f0)
1 parent f2330e6 commit 2dff8fc

File tree

7 files changed

+190
-8
lines changed

7 files changed

+190
-8
lines changed

clang/lib/Parse/ParseObjc.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,9 +1873,9 @@ void Parser::HelperActionsForIvarDeclarations(Decl *interfaceDecl, SourceLocatio
18731873
if (!RBraceMissing)
18741874
T.consumeClose();
18751875

1876-
Actions.ActOnObjCContainerStartDefinition(interfaceDecl);
1876+
assert(getObjCDeclContext() == interfaceDecl &&
1877+
"Ivars should have interfaceDecl as their decl context");
18771878
Actions.ActOnLastBitfield(T.getCloseLocation(), AllIvarDecls);
1878-
Actions.ActOnObjCContainerFinishDefinition();
18791879
// Call ActOnFields() even if we don't have any decls. This is useful
18801880
// for code rewriting tools that need to be aware of the empty list.
18811881
Actions.ActOnFields(getCurScope(), atLoc, interfaceDecl, AllIvarDecls,
@@ -1910,8 +1910,7 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
19101910
assert(Tok.is(tok::l_brace) && "expected {");
19111911
SmallVector<Decl *, 32> AllIvarDecls;
19121912

1913-
ParseScope ClassScope(this, Scope::DeclScope|Scope::ClassScope);
1914-
ObjCDeclContextSwitch ObjCDC(*this);
1913+
ParseScope ClassScope(this, Scope::DeclScope | Scope::ClassScope);
19151914

19161915
BalancedDelimiterTracker T(*this, tok::l_brace);
19171916
T.consumeOpen();
@@ -1975,13 +1974,13 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
19751974
}
19761975

19771976
auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
1978-
Actions.ActOnObjCContainerStartDefinition(interfaceDecl);
1977+
assert(getObjCDeclContext() == interfaceDecl &&
1978+
"Ivar should have interfaceDecl as its decl context");
19791979
// Install the declarator into the interface decl.
19801980
FD.D.setObjCIvar(true);
19811981
Decl *Field = Actions.ActOnIvar(
19821982
getCurScope(), FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D,
19831983
FD.BitfieldSize, visibility);
1984-
Actions.ActOnObjCContainerFinishDefinition();
19851984
if (Field)
19861985
AllIvarDecls.push_back(Field);
19871986
FD.complete(Field);

clang/lib/Sema/SemaDecl.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15905,9 +15905,20 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
1590515905
// with C structs, unions, and enums when looking for a matching
1590615906
// tag declaration or definition. See the similar lookup tweak
1590715907
// in Sema::LookupName; is there a better way to deal with this?
15908-
while (isa<RecordDecl>(SearchDC) || isa<EnumDecl>(SearchDC))
15908+
while (isa<RecordDecl, EnumDecl, ObjCContainerDecl>(SearchDC))
15909+
SearchDC = SearchDC->getParent();
15910+
} else if (getLangOpts().CPlusPlus) {
15911+
// Inside ObjCContainer want to keep it as a lexical decl context but go
15912+
// past it (most often to TranslationUnit) to find the semantic decl
15913+
// context.
15914+
while (isa<ObjCContainerDecl>(SearchDC))
1590915915
SearchDC = SearchDC->getParent();
1591015916
}
15917+
} else if (getLangOpts().CPlusPlus) {
15918+
// Don't use ObjCContainerDecl as the semantic decl context for anonymous
15919+
// TagDecl the same way as we skip it for named TagDecl.
15920+
while (isa<ObjCContainerDecl>(SearchDC))
15921+
SearchDC = SearchDC->getParent();
1591115922
}
1591215923

1591315924
if (Previous.isSingleResult() &&

clang/lib/Serialization/ASTCommon.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,9 @@ bool serialization::needsAnonymousDeclarationNumber(const NamedDecl *D) {
477477
// Otherwise, we only care about anonymous class members / block-scope decls.
478478
// FIXME: We need to handle lambdas and blocks within inline / templated
479479
// variables too.
480-
if (D->getDeclName() || !isa<RecordDecl>(D->getLexicalDeclContext()))
480+
if (D->getDeclName())
481+
return false;
482+
if (!isa<RecordDecl, ObjCInterfaceDecl>(D->getLexicalDeclContext()))
481483
return false;
482484
return isa<TagDecl>(D) || isa<FieldDecl>(D);
483485
}

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3438,6 +3438,8 @@ ASTDeclReader::getPrimaryDCForAnonymousDecl(DeclContext *LexicalDC) {
34383438
if (auto *RD = dyn_cast<CXXRecordDecl>(LexicalDC)) {
34393439
auto *DD = RD->getCanonicalDecl()->DefinitionData;
34403440
return DD ? DD->Definition : nullptr;
3441+
} else if (auto *OID = dyn_cast<ObjCInterfaceDecl>(LexicalDC)) {
3442+
return OID->getCanonicalDecl()->getDefinition();
34413443
}
34423444

34433445
// For anything else, walk its merged redeclarations looking for a definition.

clang/test/AST/ast-dump-decl.mm

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,23 @@ - (void) foo {
2828
// CHECK-NEXT: ObjCInterface{{.*}} 'TestObjCImplementation'
2929
// CHECK-NEXT: CXXCtorInitializer{{.*}} 'X'
3030
// CHECK-NEXT: CXXConstructExpr
31+
// CHECK-NEXT: CXXRecordDecl{{.*}} struct X definition
32+
// CHECK-NEXT: DefinitionData
33+
// CHECK-NEXT: DefaultConstructor
34+
// CHECK-NEXT: CopyConstructor
35+
// CHECK-NEXT: MoveConstructor
36+
// CHECK-NEXT: CopyAssignment
37+
// CHECK-NEXT: MoveAssignment
38+
// CHECK-NEXT: Destructor
39+
// CHECK-NEXT: CXXRecordDecl{{.*}} struct X
40+
// CHECK-NEXT: FieldDecl{{.*}} i 'int'
41+
// CHECK-NEXT: CXXConstructorDecl{{.*}} 'void ()
42+
// CHECK-NEXT: CompoundStmt
43+
// CHECK-NEXT: CXXConstructorDecl{{.*}} 'void (const X &)
44+
// CHECK-NEXT: ParmVarDecl{{.*}} 'const X &'
45+
// CHECK-NEXT: CXXConstructorDecl{{.*}} 'void (X &&)
46+
// CHECK-NEXT: ParmVarDecl{{.*}} 'X &&'
47+
// CHECK-NEXT: CXXDestructorDecl
3148
// CHECK-NEXT: ObjCIvarDecl{{.*}} X
3249
// CHECK-NEXT: ObjCMethodDecl{{.*}} foo
3350

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// UNSUPPORTED: -zos, -aix
2+
// RUN: rm -rf %t
3+
// RUN: split-file %s %t
4+
// RUN: %clang_cc1 -fsyntax-only -F%t/Frameworks %t/test.m -Wno-objc-property-implementation -Wno-incomplete-implementation \
5+
// RUN: -fmodules -fmodule-name=Target -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
6+
7+
// RUN: %clang_cc1 -fsyntax-only -F%t/Frameworks -x objective-c++ %t/test.m -Wno-objc-property-implementation -Wno-incomplete-implementation \
8+
// RUN: -fmodules -fmodule-name=Target -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
9+
10+
// Test anonymous TagDecl inside Objective-C interfaces are merged and ivars with these anonymous types are merged too.
11+
12+
//--- Frameworks/Foundation.framework/Headers/Foundation.h
13+
@interface NSObject
14+
@end
15+
16+
//--- Frameworks/Foundation.framework/Modules/module.modulemap
17+
framework module Foundation {
18+
header "Foundation.h"
19+
export *
20+
}
21+
22+
//--- Frameworks/Target.framework/Headers/Target.h
23+
#import <Foundation/Foundation.h>
24+
@interface TestClass : NSObject {
25+
@public
26+
struct {
27+
struct { int x; int y; } left;
28+
struct { int w; int z; } right;
29+
} structIvar;
30+
union { int x; float y; } *unionIvar;
31+
enum { kX = 0, } enumIvar;
32+
}
33+
@property struct { int u; } prop;
34+
#ifndef __cplusplus
35+
- (struct { int v; })method;
36+
#endif
37+
@end
38+
39+
@interface TestClass() {
40+
@public
41+
struct { int y; } extensionIvar;
42+
}
43+
@end
44+
45+
@implementation TestClass {
46+
@public
47+
struct { int z; } implementationIvar;
48+
}
49+
@end
50+
51+
//--- Frameworks/Target.framework/Modules/module.modulemap
52+
framework module Target {
53+
header "Target.h"
54+
export *
55+
}
56+
57+
//--- Frameworks/Redirect.framework/Headers/Redirect.h
58+
#import <Target/Target.h>
59+
60+
//--- Frameworks/Redirect.framework/Modules/module.modulemap
61+
framework module Redirect {
62+
header "Redirect.h"
63+
export *
64+
}
65+
66+
//--- test.m
67+
// At first import everything as non-modular.
68+
#import <Target/Target.h>
69+
// And now as modular to merge same entities obtained through different sources.
70+
#import <Redirect/Redirect.h>
71+
// Non-modular import is achieved through using the same name (-fmodule-name) as the imported framework module.
72+
73+
void test(TestClass *obj) {
74+
obj->structIvar.left.x = 0;
75+
obj->unionIvar->y = 1.0f;
76+
obj->enumIvar = kX;
77+
int tmp = obj.prop.u;
78+
#ifndef __cplusplus
79+
tmp += [obj method].v;
80+
#endif
81+
82+
obj->extensionIvar.y = 0;
83+
obj->implementationIvar.z = 0;
84+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class -x objective-c++ %s
3+
4+
// Test decls inside Objective-C entities are considered to be duplicates of same-name decls outside of these entities.
5+
6+
@protocol SomeProtocol
7+
struct InProtocol {}; // expected-note {{previous definition is here}}
8+
- (union MethodReturnType { int x; float y; })returningMethod; // expected-note {{previous definition is here}}
9+
#ifdef __cplusplus
10+
// expected-error@-2 {{'MethodReturnType' cannot be defined in a parameter type}}
11+
#endif
12+
@end
13+
14+
@interface Container {
15+
struct InInterfaceCurliesWithField {} field; // expected-note {{previous definition is here}}
16+
union InInterfaceCurlies { int x; float y; }; // expected-note {{previous definition is here}}
17+
}
18+
enum InInterface { kX = 0, }; // expected-note {{previous definition is here}}
19+
#ifdef __cplusplus
20+
enum class InInterfaceScoped { kXScoped = 0, }; // expected-note {{previous definition is here}}
21+
#endif
22+
@end
23+
24+
@interface Container(Category)
25+
union InCategory { int x; float y; }; // expected-note {{previous definition is here}}
26+
@end
27+
28+
@interface Container() {
29+
enum InExtensionCurliesWithField: int { kY = 1, } extensionField; // expected-note {{previous definition is here}}
30+
struct InExtensionCurlies {}; // expected-note {{previous definition is here}}
31+
}
32+
union InExtension { int x; float y; }; // expected-note {{previous definition is here}}
33+
@end
34+
35+
@implementation Container {
36+
union InImplementationCurliesWithField { int x; float y; } implField; // expected-note {{previous definition is here}}
37+
enum InImplementationCurlies { kZ = 2, }; // expected-note {{previous definition is here}}
38+
}
39+
struct InImplementation {}; // expected-note {{previous definition is here}}
40+
@end
41+
42+
@implementation Container(Category)
43+
enum InCategoryImplementation { kW = 3, }; // expected-note {{previous definition is here}}
44+
@end
45+
46+
47+
struct InProtocol { int a; }; // expected-error {{redefinition of 'InProtocol'}}
48+
union MethodReturnType { int a; long b; }; // expected-error {{redefinition of 'MethodReturnType'}}
49+
50+
struct InInterfaceCurliesWithField { int a; }; // expected-error {{redefinition of 'InInterfaceCurliesWithField'}}
51+
union InInterfaceCurlies { int a; long b; }; // expected-error {{redefinition of 'InInterfaceCurlies'}}
52+
enum InInterface { kA = 10, }; // expected-error {{redefinition of 'InInterface'}}
53+
#ifdef __cplusplus
54+
enum class InInterfaceScoped { kAScoped = 10, }; // expected-error {{redefinition of 'InInterfaceScoped'}}
55+
#endif
56+
57+
union InCategory { int a; long b; }; // expected-error {{redefinition of 'InCategory'}}
58+
59+
enum InExtensionCurliesWithField: int { kB = 11, }; // expected-error {{redefinition of 'InExtensionCurliesWithField'}}
60+
struct InExtensionCurlies { int a; }; // expected-error {{redefinition of 'InExtensionCurlies'}}
61+
union InExtension { int a; long b; }; // expected-error {{redefinition of 'InExtension'}}
62+
63+
union InImplementationCurliesWithField { int a; long b; }; // expected-error {{redefinition of 'InImplementationCurliesWithField'}}
64+
enum InImplementationCurlies { kC = 12, }; // expected-error {{redefinition of 'InImplementationCurlies'}}
65+
struct InImplementation { int a; }; // expected-error {{redefinition of 'InImplementation'}}
66+
67+
enum InCategoryImplementation { kD = 13, }; // expected-error {{redefinition of 'InCategoryImplementation'}}

0 commit comments

Comments
 (0)