Skip to content

Commit 93764ff

Browse files
committed
[modules] Fix miscompilation when using two RecordDecl definitions with the same name.
When deserializing a RecordDecl we don't enforce that redeclaration chain contains only a single definition. So if the canonical decl is not a definition itself, `RecordType::getDecl` can return different objects before and after an include. It means we can build CGRecordLayout for one RecordDecl with its set of FieldDecl but try to use it with FieldDecl belonging to a different RecordDecl. With assertions enabled it results in > Assertion failed: (FieldInfo.count(FD) && "Invalid field for record!"), > function getLLVMFieldNo, file llvm-project/clang/lib/CodeGen/CGRecordLayout.h, line 199. and with assertions disabled a bunch of fields are treated as their memory is located at offset 0. Fix by keeping the first encountered RecordDecl definition and marking the subsequent ones as non-definitions. Also need to merge FieldDecl properly, so that `getPrimaryMergedDecl` works correctly and during name lookup we don't treat fields from same-name RecordDecl as ambiguous. rdar://80184238 Differential Revision: https://reviews.llvm.org/D106994
1 parent b5da312 commit 93764ff

File tree

15 files changed

+211
-3
lines changed

15 files changed

+211
-3
lines changed

clang/include/clang/Serialization/ASTReader.h

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

1165+
/// A mapping from canonical declarations of records to their canonical
1166+
/// definitions. Doesn't cover CXXRecordDecl.
1167+
llvm::DenseMap<RecordDecl *, RecordDecl *> RecordDefinitions;
1168+
11651169
/// When reading a Stmt tree, Stmt operands are placed in this stack.
11661170
SmallVector<Stmt *, 16> StmtStack;
11671171

clang/lib/Serialization/ASTCommon.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ bool serialization::needsAnonymousDeclarationNumber(const NamedDecl *D) {
474474
// Otherwise, we only care about anonymous class members / block-scope decls.
475475
// FIXME: We need to handle lambdas and blocks within inline / templated
476476
// variables too.
477-
if (D->getDeclName() || !isa<CXXRecordDecl>(D->getLexicalDeclContext()))
477+
if (D->getDeclName() || !isa<RecordDecl>(D->getLexicalDeclContext()))
478478
return false;
479479
return isa<TagDecl>(D) || isa<FieldDecl>(D);
480480
}

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->setCompleteDefinition(false);
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
@@ -2645,7 +2673,7 @@ static bool allowODRLikeMergeInC(NamedDecl *ND) {
26452673
if (!ND)
26462674
return false;
26472675
// TODO: implement merge for other necessary decls.
2648-
if (isa<EnumConstantDecl>(ND))
2676+
if (isa<EnumConstantDecl, FieldDecl, IndirectFieldDecl>(ND))
26492677
return true;
26502678
return false;
26512679
}
@@ -3315,6 +3343,9 @@ DeclContext *ASTDeclReader::getPrimaryContextForMerging(ASTReader &Reader,
33153343
return DD->Definition;
33163344
}
33173345

3346+
if (auto *RD = dyn_cast<RecordDecl>(DC))
3347+
return RD->getDefinition();
3348+
33183349
if (auto *ED = dyn_cast<EnumDecl>(DC))
33193350
return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition()
33203351
: nullptr;
@@ -3398,6 +3429,9 @@ ASTDeclReader::getPrimaryDCForAnonymousDecl(DeclContext *LexicalDC) {
33983429
if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
33993430
if (MD->isThisDeclarationADefinition())
34003431
return MD;
3432+
if (auto *RD = dyn_cast<RecordDecl>(D))
3433+
if (RD->isThisDeclarationADefinition())
3434+
return RD;
34013435
}
34023436

34033437
// 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+
}

0 commit comments

Comments
 (0)