Skip to content

Commit 29444f0

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
1 parent c7bd9dc commit 29444f0

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
@@ -1871,9 +1871,9 @@ void Parser::HelperActionsForIvarDeclarations(Decl *interfaceDecl, SourceLocatio
18711871
if (!RBraceMissing)
18721872
T.consumeClose();
18731873

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

1911-
ParseScope ClassScope(this, Scope::DeclScope|Scope::ClassScope);
1912-
ObjCDeclContextSwitch ObjCDC(*this);
1911+
ParseScope ClassScope(this, Scope::DeclScope | Scope::ClassScope);
19131912

19141913
BalancedDelimiterTracker T(*this, tok::l_brace);
19151914
T.consumeOpen();
@@ -1973,13 +1972,13 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
19731972
}
19741973

19751974
auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
1976-
Actions.ActOnObjCContainerStartDefinition(interfaceDecl);
1975+
assert(getObjCDeclContext() == interfaceDecl &&
1976+
"Ivar should have interfaceDecl as its decl context");
19771977
// Install the declarator into the interface decl.
19781978
FD.D.setObjCIvar(true);
19791979
Decl *Field = Actions.ActOnIvar(
19801980
getCurScope(), FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D,
19811981
FD.BitfieldSize, visibility);
1982-
Actions.ActOnObjCContainerFinishDefinition();
19831982
if (Field)
19841983
AllIvarDecls.push_back(Field);
19851984
FD.complete(Field);

clang/lib/Sema/SemaDecl.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16059,9 +16059,20 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
1605916059
// with C structs, unions, and enums when looking for a matching
1606016060
// tag declaration or definition. See the similar lookup tweak
1606116061
// in Sema::LookupName; is there a better way to deal with this?
16062-
while (isa<RecordDecl>(SearchDC) || isa<EnumDecl>(SearchDC))
16062+
while (isa<RecordDecl, EnumDecl, ObjCContainerDecl>(SearchDC))
16063+
SearchDC = SearchDC->getParent();
16064+
} else if (getLangOpts().CPlusPlus) {
16065+
// Inside ObjCContainer want to keep it as a lexical decl context but go
16066+
// past it (most often to TranslationUnit) to find the semantic decl
16067+
// context.
16068+
while (isa<ObjCContainerDecl>(SearchDC))
1606316069
SearchDC = SearchDC->getParent();
1606416070
}
16071+
} else if (getLangOpts().CPlusPlus) {
16072+
// Don't use ObjCContainerDecl as the semantic decl context for anonymous
16073+
// TagDecl the same way as we skip it for named TagDecl.
16074+
while (isa<ObjCContainerDecl>(SearchDC))
16075+
SearchDC = SearchDC->getParent();
1606516076
}
1606616077

1606716078
if (Previous.isSingleResult() &&

clang/lib/Serialization/ASTCommon.cpp

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

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,8 @@ ASTDeclReader::getPrimaryDCForAnonymousDecl(DeclContext *LexicalDC) {
30653065
if (auto *RD = dyn_cast<CXXRecordDecl>(LexicalDC)) {
30663066
auto *DD = RD->getCanonicalDecl()->DefinitionData;
30673067
return DD ? DD->Definition : nullptr;
3068+
} else if (auto *OID = dyn_cast<ObjCInterfaceDecl>(LexicalDC)) {
3069+
return OID->getCanonicalDecl()->getDefinition();
30683070
}
30693071

30703072
// 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
@@ -30,6 +30,23 @@ - (void) foo {
3030
// CHECK-NEXT: ObjCInterface{{.*}} 'TestObjCImplementation'
3131
// CHECK-NEXT: CXXCtorInitializer{{.*}} 'X'
3232
// CHECK-NEXT: CXXConstructExpr
33+
// CHECK-NEXT: CXXRecordDecl{{.*}} struct X definition
34+
// CHECK-NEXT: DefinitionData
35+
// CHECK-NEXT: DefaultConstructor
36+
// CHECK-NEXT: CopyConstructor
37+
// CHECK-NEXT: MoveConstructor
38+
// CHECK-NEXT: CopyAssignment
39+
// CHECK-NEXT: MoveAssignment
40+
// CHECK-NEXT: Destructor
41+
// CHECK-NEXT: CXXRecordDecl{{.*}} struct X
42+
// CHECK-NEXT: FieldDecl{{.*}} i 'int'
43+
// CHECK-NEXT: CXXConstructorDecl{{.*}} 'void ()
44+
// CHECK-NEXT: CompoundStmt
45+
// CHECK-NEXT: CXXConstructorDecl{{.*}} 'void (const X &)
46+
// CHECK-NEXT: ParmVarDecl{{.*}} 'const X &'
47+
// CHECK-NEXT: CXXConstructorDecl{{.*}} 'void (X &&)
48+
// CHECK-NEXT: ParmVarDecl{{.*}} 'X &&'
49+
// CHECK-NEXT: CXXDestructorDecl
3350
// CHECK-NEXT: ObjCIvarDecl{{.*}} X
3451
// CHECK-NEXT: ObjCMethodDecl{{.*}} foo
3552

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)