Skip to content

Conversation

@Backl1ght
Copy link
Member

@Backl1ght Backl1ght commented Nov 8, 2025

Fixes #166784

After performing initialization of a B using {.b = 42} successfully, we will set FieldDecl of designator b to B::b, and then use the saved FieldDecl in later actions. When it comes to performing initialization of a A using {.b = 42}, we do not clear the saved FieldDecl, use it without any check, which is wrong as there is no B::b inside A.

As a result, we perform the initialization successfully, this makes both f<A> and f<B> viable and finally makes the call to f<{.b = 42}> ambiguous.

This pr add a check to make sure we use correct FieldDecl of designators. I also tried some other methods, but all of them do not work:

  • I tried clearing saved FieldDecl after we finish initialization, but I can not find a proper place.
  • I tried looking up FieldDecl of designators each time, but anonymous structs/unions set FieldDecl in other place and require us to use the saved FieldDecl.

FieldDecl *KnownField = D->getFieldDecl();
if (!KnownField) {
const IdentifierInfo *FieldName = D->getFieldName();
ValueDecl *VD = SemaRef.tryLookupUnambiguousFieldDecl(RD, FieldName);
if (auto *FD = dyn_cast_if_present<FieldDecl>(VD)) {
KnownField = FD;
} else if (auto *IFD = dyn_cast_if_present<IndirectFieldDecl>(VD)) {
// In verify mode, don't modify the original.
if (VerifyOnly)
DIE = CloneDesignatedInitExpr(SemaRef, DIE);
ExpandAnonymousFieldDesignator(SemaRef, DIE, DesigIdx, IFD);
D = DIE->getDesignator(DesigIdx);
KnownField = cast<FieldDecl>(*IFD->chain_begin());
}
if (!KnownField) {
if (VerifyOnly) {
++Index;
return true; // No typo correction when just trying this out.
}

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

llvmbot commented Nov 8, 2025

@llvm/pr-subscribers-clang

Author: Zhikai Zeng (Backl1ght)

Changes

Fixes #166784

The cause is that we will cache KnownField after check of B, and use the cache incorrectly when performing check of type A. This pr add a check to make sure we use correct cache.

FieldDecl *KnownField = D->getFieldDecl();
if (!KnownField) {
const IdentifierInfo *FieldName = D->getFieldName();
ValueDecl *VD = SemaRef.tryLookupUnambiguousFieldDecl(RD, FieldName);
if (auto *FD = dyn_cast_if_present<FieldDecl>(VD)) {
KnownField = FD;
} else if (auto *IFD = dyn_cast_if_present<IndirectFieldDecl>(VD)) {
// In verify mode, don't modify the original.
if (VerifyOnly)
DIE = CloneDesignatedInitExpr(SemaRef, DIE);
ExpandAnonymousFieldDesignator(SemaRef, DIE, DesigIdx, IFD);
D = DIE->getDesignator(DesigIdx);
KnownField = cast<FieldDecl>(*IFD->chain_begin());
}
if (!KnownField) {
if (VerifyOnly) {
++Index;
return true; // No typo correction when just trying this out.
}


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

4 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/lib/Sema/SemaInit.cpp (+2)
  • (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+8-1)
  • (modified) clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp (+23)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6b396e7ba63f3..869223512ab29 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -459,6 +459,8 @@ Bug Fixes in This Version
 - Fixed a crash triggered by unterminated ``__has_embed``. (#GH162953)
 - Accept empty enumerations in MSVC-compatible C mode. (#GH114402)
 - Fixed false-positive shadow diagnostics for lambdas in explicit object member functions. (#GH163731)
+- Fixed a incorrect diagnostic for ambiguous function call that use a 
+  designated-initializer as template argument. (#GH166784)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index cc6ddf568d346..58c1c01c9b3ba 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -2937,6 +2937,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
     }
 
     FieldDecl *KnownField = D->getFieldDecl();
+    if (KnownField && KnownField->getParent() != RD)
+      KnownField = nullptr;
     if (!KnownField) {
       const IdentifierInfo *FieldName = D->getFieldName();
       ValueDecl *VD = SemaRef.tryLookupUnambiguousFieldDecl(RD, FieldName);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 4d58f00168298..2d3d8ef5279ce 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -7060,8 +7060,15 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
     // anonymous unions in class templates).
   }
 
-  if (!ParentDependsOnArgs)
+  if (!ParentDependsOnArgs) {
+    if (auto Found =
+            CurrentInstantiationScope
+                ? CurrentInstantiationScope->getInstantiationOfIfExists(D)
+                : nullptr) {
+        return cast<NamedDecl>(Found->dyn_cast<Decl *>());
+    }
     return D;
+  }
 
   ParentDC = FindInstantiatedContext(Loc, ParentDC, TemplateArgs);
   if (!ParentDC)
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
index c4ac36e263bc8..613956324cb35 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
@@ -134,3 +134,26 @@ namespace error_on_type_instantiation {
   template void g<int>();
   // expected-note@-1 {{in instantiation of function template specialization}}
 }
+
+namespace GH166784 {
+
+struct A {
+  int a;
+};
+struct B {
+  int b;
+};
+template <A a> void f() {
+  static_assert(a.a == 42);
+}
+template <B b> void f() {
+  static_assert(b.b == 42);
+}
+
+using T1 = decltype(f<{.a = 42}>());
+using T2 = decltype(f<A{.a = 42}>());
+
+using T3 = decltype(f<{.b = 42}>());
+using T4 = decltype(f<B{.b = 42}>());
+
+} // namespace GH166784

Comment on lines 462 to 463
- Fixed an incorrect diagnostic for ambiguous function call that use a
designated-initializer as template argument. (#GH166784)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Fixed an incorrect diagnostic for ambiguous function call that use a
designated-initializer as template argument. (#GH166784)
- Fixed an incorrect diagnostic for ambiguous function call that uses a
designated-initializer as template argument. (#GH166784)

Copy link
Member Author

Choose a reason for hiding this comment

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

done.

Comment on lines +2940 to +2941
if (KnownField && KnownField->getParent() != RD)
KnownField = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

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

We need some comments/explanation for this workaround.

If this is related to cache, is it possible for us to fix that instead?

Copy link
Member Author

@Backl1ght Backl1ght Nov 9, 2025

Choose a reason for hiding this comment

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

We need some comments/explanation for this workaround.

I have update a more detailed pr description.

If this is related to cache, is it possible for us to fix that instead?

No I think, the saved FieldDecl will be used in later actions while anonymous structs/unions require us to use saved FieldDecl. Another possible way is clear the saved FieldDecl somewhere, but I can not find a proper place.

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.

Using a designated-initializer-list as a template argument makes the function call ambiguous

3 participants