Skip to content

Commit 0aa51e8

Browse files
committed
Reland "[modules] Fix miscompilation when using two RecordDecl definitions with the same name."
This reverts commit a0423cc and re-applies commit 9454716. After adding `isThisDeclarationADemotedDefinition` API now swift can query if a declaration was a definition but was demoted. This way it can keep accepting `RecordDecl` that were merged across different modules. rdar://86548228
1 parent b40f8a5 commit 0aa51e8

File tree

15 files changed

+214
-3
lines changed

15 files changed

+214
-3
lines changed

clang/include/clang/Serialization/ASTReader.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,10 @@ class ASTReader
11551155
/// definitions. Only populated when using modules in C++.
11561156
llvm::DenseMap<EnumDecl *, EnumDecl *> EnumDefinitions;
11571157

1158+
/// A mapping from canonical declarations of records to their canonical
1159+
/// definitions. Doesn't cover CXXRecordDecl.
1160+
llvm::DenseMap<RecordDecl *, RecordDecl *> RecordDefinitions;
1161+
11581162
/// When reading a Stmt tree, Stmt operands are placed in this stack.
11591163
SmallVector<Stmt *, 16> StmtStack;
11601164

clang/lib/Serialization/ASTCommon.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ 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<CXXRecordDecl>(D->getLexicalDeclContext()))
480+
if (D->getDeclName() || !isa<RecordDecl>(D->getLexicalDeclContext()))
481481
return false;
482482
return isa<TagDecl>(D) || isa<FieldDecl>(D);
483483
}

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ namespace clang {
332332
RedeclarableResult VisitTagDecl(TagDecl *TD);
333333
void VisitEnumDecl(EnumDecl *ED);
334334
RedeclarableResult VisitRecordDeclImpl(RecordDecl *RD);
335-
void VisitRecordDecl(RecordDecl *RD) { VisitRecordDeclImpl(RD); }
335+
void VisitRecordDecl(RecordDecl *RD);
336336
RedeclarableResult VisitCXXRecordDeclImpl(CXXRecordDecl *D);
337337
void VisitCXXRecordDecl(CXXRecordDecl *D) { VisitCXXRecordDeclImpl(D); }
338338
RedeclarableResult VisitClassTemplateSpecializationDeclImpl(
@@ -808,6 +808,34 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) {
808808
return Redecl;
809809
}
810810

811+
void ASTDeclReader::VisitRecordDecl(RecordDecl *RD) {
812+
VisitRecordDeclImpl(RD);
813+
814+
// Maintain the invariant of a redeclaration chain containing only
815+
// a single definition.
816+
if (RD->isCompleteDefinition()) {
817+
RecordDecl *Canon = static_cast<RecordDecl *>(RD->getCanonicalDecl());
818+
RecordDecl *&OldDef = Reader.RecordDefinitions[Canon];
819+
if (!OldDef) {
820+
// This is the first time we've seen an imported definition. Look for a
821+
// local definition before deciding that we are the first definition.
822+
for (auto *D : merged_redecls(Canon)) {
823+
if (!D->isFromASTFile() && D->isCompleteDefinition()) {
824+
OldDef = D;
825+
break;
826+
}
827+
}
828+
}
829+
if (OldDef) {
830+
Reader.MergedDeclContexts.insert(std::make_pair(RD, OldDef));
831+
RD->demoteThisDefinitionToDeclaration();
832+
Reader.mergeDefinitionVisibility(OldDef, RD);
833+
} else {
834+
OldDef = RD;
835+
}
836+
}
837+
}
838+
811839
void ASTDeclReader::VisitValueDecl(ValueDecl *VD) {
812840
VisitNamedDecl(VD);
813841
// For function declarations, defer reading the type in case the function has
@@ -2661,7 +2689,7 @@ static bool allowODRLikeMergeInC(NamedDecl *ND) {
26612689
if (!ND)
26622690
return false;
26632691
// TODO: implement merge for other necessary decls.
2664-
if (isa<EnumConstantDecl>(ND))
2692+
if (isa<EnumConstantDecl, FieldDecl, IndirectFieldDecl>(ND))
26652693
return true;
26662694
return false;
26672695
}
@@ -3333,6 +3361,9 @@ DeclContext *ASTDeclReader::getPrimaryContextForMerging(ASTReader &Reader,
33333361
return DD->Definition;
33343362
}
33353363

3364+
if (auto *RD = dyn_cast<RecordDecl>(DC))
3365+
return RD->getDefinition();
3366+
33363367
if (auto *ED = dyn_cast<EnumDecl>(DC))
33373368
return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition()
33383369
: nullptr;
@@ -3419,6 +3450,9 @@ ASTDeclReader::getPrimaryDCForAnonymousDecl(DeclContext *LexicalDC) {
34193450
if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
34203451
if (MD->isThisDeclarationADefinition())
34213452
return MD;
3453+
if (auto *RD = dyn_cast<RecordDecl>(D))
3454+
if (RD->isThisDeclarationADefinition())
3455+
return RD;
34223456
}
34233457

34243458
// No merged definition yet.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// It is important to have a definition *after* non-definition declaration.
2+
typedef struct _Buffer Buffer;
3+
struct _Buffer {
4+
int a;
5+
int b;
6+
int c;
7+
};
8+
9+
typedef struct _AnonymousStruct AnonymousStruct;
10+
struct _AnonymousStruct {
11+
struct {
12+
int x;
13+
int y;
14+
};
15+
};
16+
17+
typedef union _UnionRecord UnionRecord;
18+
union _UnionRecord {
19+
int u: 2;
20+
int v: 4;
21+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
framework module RecordDef {
2+
header "RecordDef.h"
3+
export *
4+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// It is important to have a definition *after* non-definition declaration.
2+
typedef struct _Buffer Buffer;
3+
struct _Buffer {
4+
int a;
5+
int b;
6+
int c;
7+
};
8+
9+
typedef struct _AnonymousStruct AnonymousStruct;
10+
struct _AnonymousStruct {
11+
struct {
12+
int x;
13+
int y;
14+
};
15+
};
16+
17+
typedef union _UnionRecord UnionRecord;
18+
union _UnionRecord {
19+
int u: 2;
20+
int v: 4;
21+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
framework module RecordDefCopy {
2+
header "RecordDefCopy.h"
3+
export *
4+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// It is important to have a definition *after* non-definition declaration.
2+
typedef struct _Buffer Buffer;
3+
struct _Buffer {
4+
int a;
5+
int b;
6+
int c;
7+
};
8+
9+
typedef struct _AnonymousStruct AnonymousStruct;
10+
struct _AnonymousStruct {
11+
struct {
12+
int x;
13+
int y;
14+
};
15+
};
16+
17+
typedef union _UnionRecord UnionRecord;
18+
union _UnionRecord {
19+
int u: 2;
20+
int v: 4;
21+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Empty header to create a module.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
framework module RecordDefHidden {
2+
header "Visible.h"
3+
export *
4+
5+
explicit module Hidden {
6+
header "Hidden.h"
7+
export *
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import <RecordDef/RecordDef.h>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
framework module RecordDefIncluder {
2+
header "RecordDefIncluder.h"
3+
export *
4+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// UNSUPPORTED: -zos, -aix
2+
// RUN: rm -rf %t
3+
// RUN: mkdir %t
4+
// RUN: %clang_cc1 -emit-llvm -o %t/test.bc -F%S/Inputs/merge-record-definition %s \
5+
// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache -fmodule-name=RecordDef
6+
// RUN: %clang_cc1 -emit-llvm -o %t/test.bc -F%S/Inputs/merge-record-definition %s -DMODULAR_BEFORE_TEXTUAL \
7+
// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache -fmodule-name=RecordDef
8+
9+
// Test a case when a struct definition once is included from a textual header and once from a module.
10+
11+
#ifdef MODULAR_BEFORE_TEXTUAL
12+
#import <RecordDefIncluder/RecordDefIncluder.h>
13+
#else
14+
#import <RecordDef/RecordDef.h>
15+
#endif
16+
17+
void bibi(void) {
18+
Buffer buf;
19+
buf.b = 1;
20+
AnonymousStruct strct;
21+
strct.x = 1;
22+
UnionRecord rec;
23+
rec.u = 1;
24+
}
25+
26+
#ifdef MODULAR_BEFORE_TEXTUAL
27+
#import <RecordDef/RecordDef.h>
28+
#else
29+
#import <RecordDefIncluder/RecordDefIncluder.h>
30+
#endif
31+
32+
void mbap(void) {
33+
Buffer buf;
34+
buf.c = 2;
35+
AnonymousStruct strct;
36+
strct.y = 2;
37+
UnionRecord rec;
38+
rec.v = 2;
39+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// UNSUPPORTED: -zos, -aix
2+
// RUN: rm -rf %t
3+
// RUN: mkdir %t
4+
// RUN: %clang_cc1 -emit-llvm -o %t/test.bc -F%S/Inputs/merge-record-definition %s \
5+
// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
6+
7+
// Test a case when a struct definition is first imported as invisible and then as visible.
8+
9+
#import <RecordDefHidden/Visible.h>
10+
#import <RecordDef/RecordDef.h>
11+
12+
void bibi(void) {
13+
Buffer buf;
14+
buf.b = 1;
15+
AnonymousStruct strct;
16+
strct.y = 1;
17+
UnionRecord rec;
18+
rec.u = 1;
19+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// UNSUPPORTED: -zos, -aix
2+
// RUN: rm -rf %t
3+
// RUN: mkdir %t
4+
// RUN: %clang_cc1 -emit-llvm -o %t/test.bc -F%S/Inputs/merge-record-definition %s \
5+
// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
6+
7+
// Test a case when a struct definition is present in two different modules.
8+
9+
#import <RecordDef/RecordDef.h>
10+
11+
void bibi(void) {
12+
Buffer buf;
13+
buf.b = 1;
14+
AnonymousStruct strct;
15+
strct.x = 1;
16+
UnionRecord rec;
17+
rec.u = 1;
18+
}
19+
20+
#import <RecordDefCopy/RecordDefCopy.h>
21+
22+
void mbap(void) {
23+
Buffer buf;
24+
buf.c = 2;
25+
AnonymousStruct strct;
26+
strct.y = 2;
27+
UnionRecord rec;
28+
rec.v = 2;
29+
}

0 commit comments

Comments
 (0)