Skip to content

[clang] Refine handling of C++20 aggregate initialization #131320

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 2 commits into from
Mar 25, 2025

Conversation

offsetof
Copy link
Contributor

@offsetof offsetof commented Mar 14, 2025

  • Move parts of InitializationSequence::InitializeFrom corresponding to C++ [dcl.init.general] p16.6.1 and p16.6.2 into a separate function,TryConstructorOrParenListInitialization
  • Use it in TryListInitialization to implement [dcl.init.list] p3.2
  • Fix parenthesized aggregate initialization being attempted in copy-initialization contexts or when the constructor call is ambiguous

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 14, 2025

@llvm/pr-subscribers-clang

Author: None (offsetof)

Changes

Fixes #88089


Full diff: https://github.com/llvm/llvm-project/pull/131320.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaInit.cpp (+2-1)
  • (modified) clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp (+29-13)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 56ec33fe37bf3..11db8608960ce 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6714,7 +6714,8 @@ void InitializationSequence::InitializeFrom(Sema &S,
         OverloadCandidateSet::iterator Best;
         OverloadingResult OR = getFailedCandidateSet().BestViableFunction(
             S, Kind.getLocation(), Best);
-        if (OR != OverloadingResult::OR_Deleted) {
+        if (OR != OverloadingResult::OR_Deleted &&
+            Kind.getKind() == InitializationKind::IK_Direct) {
           // C++20 [dcl.init] 17.6.2.2:
           //   - Otherwise, if no constructor is viable, the destination type is
           //   an
diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
index 1ad205d769c38..1ef74bb66b4de 100644
--- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
@@ -1,18 +1,34 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
 
-// If the initializer is (), the object is value-initialized.
-
-// expected-no-diagnostics
 namespace GH69890 {
-struct A {
-    constexpr A() {}
-    int x;
-};
+    // If the initializer is (), the object is value-initialized.
+    struct A {
+        constexpr A() {}
+        int x;
+    };
+
+    struct B : A {
+        int y;
+    };
+
+    static_assert(B().x == 0);
+    static_assert(B().y == 0);
+} // namespace GH69890
+
+namespace P0960R3 {
+    struct A { // expected-note 9 {{candidate constructor}}
+        int i;
+        operator int() volatile;
+    };
+
+    volatile A va;
+    A a = va; // expected-error {{no matching constructor for initialization of 'A'}}
 
-struct B : A {
-    int y;
-};
+    A f() {
+        return va; // expected-error {{no matching constructor for initialization of 'A'}}
+    }
 
-static_assert(B().x == 0);
-static_assert(B().y == 0);
-}
+    int g(A); // expected-note {{passing argument to parameter here}}
+    int g(auto&&);
+    int i = g(va); // expected-error {{no matching constructor for initialization of 'A'}}
+} // namespace P0960R3

@cor3ntin
Copy link
Contributor

cor3ntin commented Mar 14, 2025

I don't think this resolves the original issue.
Part of the problem is that we are missing a diagnostic saying the implicit constructor is deleted (and why).

Comment on lines 6717 to 6718
if (OR != OverloadingResult::OR_Deleted &&
Kind.getKind() == InitializationKind::IK_Direct) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably, this is https://eel.is/c++draft/dcl.init.general#16.6.2.sentence-1
Why do you think copy should not be valid here?

Afaik, the core issue @frederick-vs-ja mentioned here, #88089 (comment), only applies to the array case.

Copy link
Contributor Author

@offsetof offsetof Mar 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is https://eel.is/c++draft/dcl.init.general#16.6.2.2.sentence-1, specifically "and the initializer is a parenthesized expression-list". The bug is that X x = y; is treated as X x(y); when y has type cv X and X has no viable constructor for the initialization. (I should have explained that in the OP; sorry.)
The linked core issue is indeed related to arrays (handled by a different bullet) and is not relevant here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I missed that. Thanks for the explanation.

Can you

  • Add that wording sentence as a comment
  • Add tests demonstrating the parent-init case and brace-init cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discovered a couple more issues in this area, and updated the PR with a more comprehensive fix, along with more tests and a release note as requested.

@offsetof
Copy link
Contributor Author

I don't think this resolves the original issue.
Part of the problem is that we are missing a diagnostic saying the implicit constructor is deleted (and why).

Fair enough; I linked that issue because the nonsensical diagnostic mentioned there ("no viable conversion from S to int") is a side-effect of the bug fixed by this PR. With the change applied, we get the usual "no matching constructor" with a list of candidates. But if we want something else, then the issue should remain open.

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change needs a release note.
Please add an entry to clang/docs/ReleaseNotes.rst in the section the most adapted to the change, and referencing any Github issue this change fixes. Thanks!

Comment on lines 6717 to 6718
if (OR != OverloadingResult::OR_Deleted &&
Kind.getKind() == InitializationKind::IK_Direct) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I missed that. Thanks for the explanation.

Can you

  • Add that wording sentence as a comment
  • Add tests demonstrating the parent-init case and brace-init cases?

@offsetof offsetof changed the title [clang] Avoid doing C++20 aggregate init during copy-initialization [clang] Refine handling of C++20 aggregate initialization Mar 16, 2025
@cor3ntin
Copy link
Contributor

@offsetof CI got confused. Can you try to push an empty commit? Sorry about that

* Move parts of `InitializationSequence::InitializeFrom` corresponding
  to C++ [dcl.init.general] p16.6.1 and p16.6.2 into a separate
  function, `TryConstructorOrParenListInitialization`
* Use it in `TryListInitialization` to implement [dcl.init.list] p3.2
* Fix parenthesized aggregate initialization being attempted in
  copy-initialization contexts or when the constructor call is ambiguous
@offsetof
Copy link
Contributor Author

Seems to be working now, I think.

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@cor3ntin
Copy link
Contributor

@offsetof Will you need me to merge that for you?

@offsetof
Copy link
Contributor Author

That would be great. Thank you!

@cor3ntin cor3ntin merged commit 5105ecc into llvm:main Mar 25, 2025
7 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants