Skip to content

[Clang] Correctly initialize placeholder fields from their initializers #114196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ Bug Fixes to C++ Support
- Clang now correctly ignores previous partial specializations of member templates explicitly specialized for
an implicitly instantiated class template specialization. (#GH51051)
- Fixed an assertion failure caused by invalid enum forward declarations. (#GH112208)
- Name independent data members were not correctly initialized from default member initializers. (#GH114069)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
void setInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst,
UsingShadowDecl *Pattern);

FieldDecl *getInstantiatedFromUnnamedFieldDecl(FieldDecl *Field);
FieldDecl *getInstantiatedFromUnnamedFieldDecl(FieldDecl *Field) const;

void setInstantiatedFromUnnamedFieldDecl(FieldDecl *Inst, FieldDecl *Tmpl);

Expand Down
9 changes: 6 additions & 3 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1592,14 +1592,17 @@ ASTContext::setInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst,
InstantiatedFromUsingShadowDecl[Inst] = Pattern;
}

FieldDecl *ASTContext::getInstantiatedFromUnnamedFieldDecl(FieldDecl *Field) {
FieldDecl *
ASTContext::getInstantiatedFromUnnamedFieldDecl(FieldDecl *Field) const {
return InstantiatedFromUnnamedFieldDecl.lookup(Field);
}

void ASTContext::setInstantiatedFromUnnamedFieldDecl(FieldDecl *Inst,
FieldDecl *Tmpl) {
assert(!Inst->getDeclName() && "Instantiated field decl is not unnamed");
assert(!Tmpl->getDeclName() && "Template field decl is not unnamed");
assert((!Inst->getDeclName() || Inst->isPlaceholderVar(getLangOpts())) &&
"Instantiated field decl is not unnamed");
assert((!Inst->getDeclName() || Inst->isPlaceholderVar(getLangOpts())) &&
"Template field decl is not unnamed");
assert(!InstantiatedFromUnnamedFieldDecl[Inst] &&
"Already noted what unnamed field was instantiated from");

Expand Down
29 changes: 20 additions & 9 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5560,6 +5560,24 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
Init, InitializationContext->Context);
}

static FieldDecl *FindFieldDeclInstantiationPattern(const ASTContext &Ctx,
FieldDecl *Field) {
if (FieldDecl *Pattern = Ctx.getInstantiatedFromUnnamedFieldDecl(Field))
return Pattern;
auto *ParentRD = cast<CXXRecordDecl>(Field->getParent());
CXXRecordDecl *ClassPattern = ParentRD->getTemplateInstantiationPattern();
DeclContext::lookup_result Lookup =
ClassPattern->lookup(Field->getDeclName());
auto Rng = llvm::make_filter_range(
Lookup, [](auto &&L) { return isa<FieldDecl>(*L); });
if (Rng.empty())
return nullptr;
// FIXME: this breaks clang/test/Modules/pr28812.cpp
// assert(std::distance(Rng.begin(), Rng.end()) <= 1
// && "Duplicated instantiation pattern for field decl");
return cast<FieldDecl>(*Rng.begin());
}

ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
assert(Field->hasInClassInitializer());

Expand Down Expand Up @@ -5588,15 +5606,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
// Maybe we haven't instantiated the in-class initializer. Go check the
// pattern FieldDecl to see if it has one.
if (isTemplateInstantiation(ParentRD->getTemplateSpecializationKind())) {
CXXRecordDecl *ClassPattern = ParentRD->getTemplateInstantiationPattern();
DeclContext::lookup_result Lookup =
ClassPattern->lookup(Field->getDeclName());

FieldDecl *Pattern = nullptr;
for (auto *L : Lookup) {
if ((Pattern = dyn_cast<FieldDecl>(L)))
break;
}
FieldDecl *Pattern =
FindFieldDeclInstantiationPattern(getASTContext(), Field);
assert(Pattern && "We must have set the Pattern!");
if (!Pattern->hasInClassInitializer() ||
InstantiateInClassInitializer(Loc, Field, Pattern,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) {
if (Invalid)
Field->setInvalidDecl();

if (!Field->getDeclName()) {
if (!Field->getDeclName() || Field->isPlaceholderVar(SemaRef.getLangOpts())) {
// Keep track of where this decl came from.
SemaRef.Context.setInstantiatedFromUnnamedFieldDecl(Field, D);
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,8 @@ void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) {
else if (Bits & 1)
FD->setBitWidth(Record.readExpr());

if (!FD->getDeclName()) {
if (!FD->getDeclName() ||
FD->isPlaceholderVar(Reader.getContext().getLangOpts())) {
if (auto *Tmpl = readDeclAs<FieldDecl>())
Reader.getContext().setInstantiatedFromUnnamedFieldDecl(FD, Tmpl);
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) {
else if (D->BitField)
Record.AddStmt(D->getBitWidth());

if (!D->getDeclName())
if (!D->getDeclName() || D->isPlaceholderVar(Writer.getLangOpts()))
Record.AddDeclRef(Context.getInstantiatedFromUnnamedFieldDecl(D));

if (D->getDeclContext() == D->getLexicalDeclContext() &&
Expand Down
36 changes: 35 additions & 1 deletion clang/test/SemaCXX/cxx2c-placeholder-vars.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang -cc1 -fsyntax-only -verify -std=c++2c -Wunused-parameter -Wunused -Wpre-c++26-compat %s
// RUN: %clang_cc1 -fsyntax-only -verify -ast-dump -std=c++2c -Wunused-parameter -Wunused -Wpre-c++26-compat %s | FileCheck %s

void static_var() {
static int _; // expected-note {{previous definition is here}} \
Expand Down Expand Up @@ -254,3 +254,37 @@ namespace Bases {
}
};
}

namespace GH114069 {

template <class T>
struct A {
T _ = 1;
T _ = 2;
T : 1;
T a = 3;
T _ = 4;
};

void f() {
[[maybe_unused]] A<int> a;
}

// CHECK: NamespaceDecl {{.*}} GH114069
// CHECK: ClassTemplateSpecializationDecl {{.*}} struct A definition
// CHECK: CXXConstructorDecl {{.*}} implicit used constexpr A 'void () noexcept'
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} '_' 'int'
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} '_' 'int'
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} 'a' 'int'
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} '_' 'int'
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4
// CHECK-NEXT: CompoundStmt {{.*}}

}
Loading