https://github.com/Endilll updated 
https://github.com/llvm/llvm-project/pull/124844

>From da30f708caee020677675277673e0b7c6f9c644f Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Fri, 24 Jan 2025 15:15:17 +0400
Subject: [PATCH 01/13] [clang] Diagnose default arguments defined in different
 scopes

---
 clang/docs/ReleaseNotes.rst                   |  5 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 ++
 clang/include/clang/Sema/Overload.h           |  3 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |  5 ++
 clang/lib/Sema/SemaOverload.cpp               | 47 +++++++++++++++++++
 clang/test/CXX/drs/cwg0xx.cpp                 | 14 ++++--
 clang/test/CXX/drs/cwg4xx.cpp                 | 11 +++--
 .../SemaCXX/default-argument-extern-c.cpp     | 39 +++++++++++++++
 clang/www/cxx_dr_status.html                  |  4 +-
 9 files changed, 120 insertions(+), 12 deletions(-)
 create mode 100644 clang/test/SemaCXX/default-argument-extern-c.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f110b8cf765075..00b1e3b678dda8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -375,6 +375,11 @@ Resolutions to C++ Defect Reports
   by default.
   (`CWG2521: User-defined literals and reserved identifiers 
<https://cplusplus.github.io/CWG/issues/2521.html>`_).
 
+- Clang now diagnoses ambiguous default arguments declared in different scopes
+  when calling an ``extern "C"`` function, implementing [over.best.match] p4.
+  (`CWG1: What if two using-declarations refer to the same function but the 
declarations introduce different default-arguments? 
<https://cplusplus.github.io/CWG/issues/1.html>`_,
+  `CWG418: Imperfect wording on error on multiple default arguments on a 
called function <https://cplusplus.github.io/CWG/issues/418.html>`_)
+
 - Fix name lookup for a dependent base class that is the current instantiation.
   (`CWG591: When a dependent base class is the current instantiation 
<https://cplusplus.github.io/CWG/issues/591.html>`_).
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 774e5484cfa0e7..cfa47ec1c1f977 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5136,6 +5136,10 @@ def err_addr_ovl_not_func_ptrref : Error<
 def err_addr_ovl_no_qualifier : Error<
   "cannot form member pointer of type %0 without '&' and class name">;
 
+def err_ovl_ambiguous_default_arg
+    : Error<"function call relies on ambiguous default argument %select{|for "
+            "parameter '%1'}0">;
+
 } // let Deferrable
 
 // C++11 Literal Operators
diff --git a/clang/include/clang/Sema/Overload.h 
b/clang/include/clang/Sema/Overload.h
index c7f2422b542dd1..8e0b9ef8b44b86 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -948,7 +948,8 @@ class Sema;
     unsigned char FailureKind;
 
     /// The number of call arguments that were explicitly provided,
-    /// to be used while performing partial ordering of function templates.
+    /// to be used while performing partial ordering of function templates
+    /// and to diagnose ambiguous default arguments ([over.best.match]/4).
     unsigned ExplicitCallArguments;
 
     union {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 839b3a1cccdcc3..ba9c7c0237cd27 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -477,6 +477,11 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
       // Ignore default arguments of old decl if they are not in
       // the same scope and this is not an out-of-line definition of
       // a member function.
+      //
+      // extern "C" functions can have default arguments across different
+      // scopes, but diagnosing that early would reject well-formed code
+      // (_N5001_.[over.best.match]/4.) Instead, they are checked
+      // in BestViableFunction after the best viable function has been 
selected.
       continue;
     }
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 6ae9c51c06b315..fe8ae5a4190efe 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10928,6 +10928,53 @@ OverloadCandidateSet::BestViableFunction(Sema &S, 
SourceLocation Loc,
     S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
                                                     EquivalentCands);
 
+  // [over.match.best]/4:
+  // If the best viable function resolves to a function
+  // for which multiple declarations were found,
+  // and if any two of these declarations inhabit different scopes
+  // and specify a default argument that made the function viable,
+  // the program is ill-formed.
+  if (Best->Function && Best->Function->isExternC() &&
+      Best->ExplicitCallArguments < Best->Function->getNumParams()) {
+    // Calculate the range of parameters,
+    // default arguments of which made the candidate viable.
+    int FirstDefaultArgIndex = Best->ExplicitCallArguments;
+    int LastDefaultArgIndex = Best->Function->getNumParams() - 1;
+
+    // For each such parameter, collect all redeclarations
+    // that have non-inherited default argument.
+    llvm::SmallDenseMap<int, llvm::TinyPtrVector<ParmVarDecl *>> ParamRedecls(
+        LastDefaultArgIndex - FirstDefaultArgIndex + 1);
+    for (FunctionDecl *Redecl : Best->Function->redecls()) {
+      for (int i = FirstDefaultArgIndex; i <= LastDefaultArgIndex; ++i) {
+        ParmVarDecl *Param = Redecl->getParamDecl(i);
+        if (Param->hasDefaultArg() && !Param->hasInheritedDefaultArg())
+          ParamRedecls[i].push_back(Param);
+      }
+    }
+
+    // Emit the diagnostic if a given parameter has more than one declaration.
+    // MergeCXXFunctionDecl takes care of redeclarations of a default argument
+    // in the same scope, so if we found more than one,
+    // we assume they come from different scopes.
+    for (auto [ParamIndex, Redecls] : ParamRedecls) {
+      assert(!Redecls.empty());
+      if (Redecls.size() == 1)
+        continue;
+
+      ParmVarDecl *Param = Best->Function->getParamDecl(ParamIndex);
+      if (!Param->getDeclName().isEmpty()) {
+        S.Diag(Loc, diag::err_ovl_ambiguous_default_arg)
+            << 1 << Param->getName();
+      } else
+        S.Diag(Loc, diag::err_ovl_ambiguous_default_arg) << 0;
+      for (ParmVarDecl *Param : Redecls) {
+        S.Diag(Param->getDefaultArg()->getExprLoc(),
+               diag::note_default_argument_declared_here);
+      }
+    }
+  }
+
   return OR_Success;
 }
 
diff --git a/clang/test/CXX/drs/cwg0xx.cpp b/clang/test/CXX/drs/cwg0xx.cpp
index 15f469440c66f2..6b969925c8c191 100644
--- a/clang/test/CXX/drs/cwg0xx.cpp
+++ b/clang/test/CXX/drs/cwg0xx.cpp
@@ -11,21 +11,25 @@
 // cxx98-error@-1 {{variadic macros are a C99 feature}}
 #endif
 
-namespace cwg1 { // cwg1: no
-  namespace X { extern "C" void cwg1_f(int a = 1); }
-  namespace Y { extern "C" void cwg1_f(int a = 1); }
+namespace cwg1 { // cwg1: 20
+  namespace X { extern "C" void cwg1_f(int a = 1); } // #cwg1-X
+  namespace Y { extern "C" void cwg1_f(int a = 1); } // #cwg1-Y
   using X::cwg1_f; using Y::cwg1_f;
   void g() {
     cwg1_f(0);
-    // FIXME: This should be rejected, due to the ambiguous default argument.
     cwg1_f();
+    // expected-error@-1 {{function call relies on ambiguous default argument 
for parameter 'a'}}
+    //   expected-note@#cwg1-Y {{default argument declared here}}
+    //   expected-note@#cwg1-X {{default argument declared here}}
   }
   namespace X {
     using Y::cwg1_f;
     void h() {
       cwg1_f(0);
-      // FIXME: This should be rejected, due to the ambiguous default argument.
       cwg1_f();
+      // expected-error@-1 {{function call relies on ambiguous default 
argument for parameter 'a'}}
+      //   expected-note@#cwg1-Y {{default argument declared here}}
+      //   expected-note@#cwg1-X {{default argument declared here}}
     }
   }
 
diff --git a/clang/test/CXX/drs/cwg4xx.cpp b/clang/test/CXX/drs/cwg4xx.cpp
index bcaf7db04ad3b5..782ed70c5741b9 100644
--- a/clang/test/CXX/drs/cwg4xx.cpp
+++ b/clang/test/CXX/drs/cwg4xx.cpp
@@ -369,7 +369,7 @@ namespace cwg417 { // cwg417: no
   }
 } // namespace cwg417
 
-namespace cwg418 { // cwg418: no
+namespace cwg418 { // cwg418: 20
 namespace example1 {
 void f1(int, int = 0);
 void f1(int = 0, int);
@@ -398,10 +398,10 @@ void g2() {
 // example from [over.match.best]/4
 namespace example3 {
 namespace A {
-extern "C" void f(int = 5);
+extern "C" void f(int = 5); // #cwg418-ex3-A
 }
 namespace B {
-extern "C" void f(int = 5);
+extern "C" void f(int = 5); // #cwg418-ex3-B
 }
 
 using A::f;
@@ -409,7 +409,10 @@ using B::f;
 
 void use() {
   f(3);
-  f(); // FIXME: this should fail
+  f();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#cwg418-ex3-B {{default argument declared here}}
+  //   expected-note@#cwg418-ex3-A {{default argument declared here}}
 }
 } // namespace example3
 } // namespace cwg418
diff --git a/clang/test/SemaCXX/default-argument-extern-c.cpp 
b/clang/test/SemaCXX/default-argument-extern-c.cpp
new file mode 100644
index 00000000000000..db20c48b1ab17c
--- /dev/null
+++ b/clang/test/SemaCXX/default-argument-extern-c.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
+
+namespace A {
+  extern "C" void f1(...);
+  extern "C" void f2(int, ...);
+  extern "C" void f3(int = 0, ...); // #A-f3
+} // namespace A
+
+namespace B {
+  extern "C" void f1(...);
+  extern "C" void f2(int, ...); // #B-f2
+  extern "C" void f3(int = 0, ...); // #B-f3
+} // namespace B
+
+void f() {
+  using A::f1;
+  using A::f2;
+  using A::f3;
+  using B::f1;
+  using B::f2;
+  using B::f3;
+
+  f1();
+  f1(0);
+  f1(0, 0);
+  f2();
+  // expected-error@-1 {{no matching function for call to 'f2'}}
+  //   expected-note@#B-f2 {{candidate function not viable: requires at least 
1 argument, but 0 were provided}}
+  f2(0);
+  f2(0, 0);
+  f3();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#B-f3 {{default argument declared here}}
+  //   expected-note@#A-f3 {{default argument declared here}}
+  f3(0);
+  f3(0, 0);
+}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 69ddd5e58b921a..d9eb8c26317f3b 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -51,7 +51,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/1.html";>1</a></td>
     <td>TC1</td>
     <td>What if two using-declarations refer to the same function but the 
declarations introduce different default-arguments?</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 20</td>
   </tr>
   <tr class="open" id="2">
     <td><a href="https://cplusplus.github.io/CWG/issues/2.html";>2</a></td>
@@ -2555,7 +2555,7 @@ <h2 id="cxxdr">C++ defect report implementation 
status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/418.html";>418</a></td>
     <td>CD6</td>
     <td>Imperfect wording on error on multiple default arguments on a called 
function</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 20</td>
   </tr>
   <tr class="open" id="419">
     <td><a href="https://cplusplus.github.io/CWG/issues/419.html";>419</a></td>

>From a703624bcfe6f2e6b00c3091b63236caf3d5c09f Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Fri, 24 Jan 2025 15:32:44 +0400
Subject: [PATCH 02/13] Add a stress test

---
 .../test/SemaCXX/default-argument-extern-c.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/clang/test/SemaCXX/default-argument-extern-c.cpp 
b/clang/test/SemaCXX/default-argument-extern-c.cpp
index db20c48b1ab17c..4175b0d231ca51 100644
--- a/clang/test/SemaCXX/default-argument-extern-c.cpp
+++ b/clang/test/SemaCXX/default-argument-extern-c.cpp
@@ -37,3 +37,21 @@ void f() {
   f3(0);
   f3(0, 0);
 }
+
+#define P_10(x) x, x, x, x, x, x, x, x, x, x,
+#define P_100(x) P_10(x) P_10(x) P_10(x) P_10(x) P_10(x) \
+                 P_10(x) P_10(x) P_10(x) P_10(x) P_10(x)
+#define P_1000(x) P_100(x) P_100(x) P_100(x) P_100(x) P_100(x) \
+                  P_100(x) P_100(x) P_100(x) P_100(x) P_100(x)
+#define P_10000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) \
+                   P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x)
+
+namespace C1 {
+extern "C" int g( 
+  P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) 
P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) int = 0
+  // expected-error@-1 {{too many function parameters; subsequent parameters 
will be ignored}}
+);
+} // namespace C1
+
+using C1::g;
+int h = g();

>From c911cba6c131bcb0873b8cccde1c68d72ff6042b Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Tue, 28 Jan 2025 16:58:50 +0400
Subject: [PATCH 03/13] Take extern and local function declarations into
 account

---
 clang/include/clang/Sema/Sema.h               |  10 ++
 clang/lib/Sema/SemaExpr.cpp                   |   8 ++
 clang/lib/Sema/SemaOverload.cpp               |  77 ++++++-------
 clang/test/CXX/drs/cwg0xx.cpp                 |  15 ++-
 clang/test/CodeGenCXX/default-arguments.cpp   |  11 --
 .../default-argument-different-scopes.cpp     | 102 ++++++++++++++++++
 .../SemaCXX/default-argument-extern-c.cpp     |  57 ----------
 clang/test/SemaCXX/default1.cpp               |   7 +-
 8 files changed, 174 insertions(+), 113 deletions(-)
 create mode 100644 clang/test/SemaCXX/default-argument-different-scopes.cpp
 delete mode 100644 clang/test/SemaCXX/default-argument-extern-c.cpp

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4d6e02fe2956e0..1c94f347f7613f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10388,6 +10388,16 @@ class Sema final : public SemaBase {
                                          bool Complain = false,
                                          SourceLocation Loc = 
SourceLocation());
 
+  /// @brief Checks that each default argument needed to make the call
+  /// is defined only once, implementing [over.match.best]/4 rule.
+  ///
+  /// @param FDecl Function declaration selected for the call
+  /// @param NumArgs Number of argument explicitly specified in the call
+  /// expression
+  /// @param CallLoc Source location of the call expression
+  void checkDefaultArgumentsAcrossScopes(FunctionDecl *FDecl, int NumArgs,
+                                         SourceLocation CallLoc);
+
   // [PossiblyAFunctionType]  -->   [Return]
   // NonFunctionType --> NonFunctionType
   // R (A) --> R(A)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d5273d463d7c01..b2bed0c68490de 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5883,6 +5883,14 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
     // the call expression, before calling ConvertArgumentsForCall.
     assert((Call->getNumArgs() == NumParams) &&
            "We should have reserved space for the default arguments before!");
+
+    if (FDecl->isExternC() ||
+        std::any_of(
+            FDecl->redecls_begin(), FDecl->redecls_end(),
+            [](FunctionDecl *Redecl) { return Redecl->isLocalExternDecl(); })) 
{
+      checkDefaultArgumentsAcrossScopes(FDecl, Args.size(),
+                                        Call->getBeginLoc());
+    }
   }
 
   // If too many are passed and not variadic, error on the extras and drop
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index fe8ae5a4190efe..08cb5e54acd4be 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10928,54 +10928,55 @@ OverloadCandidateSet::BestViableFunction(Sema &S, 
SourceLocation Loc,
     S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
                                                     EquivalentCands);
 
+  return OR_Success;
+}
+
+void Sema::checkDefaultArgumentsAcrossScopes(FunctionDecl *FDecl, int NumArgs,
+                                             SourceLocation CallLoc) {
   // [over.match.best]/4:
   // If the best viable function resolves to a function
   // for which multiple declarations were found,
   // and if any two of these declarations inhabit different scopes
   // and specify a default argument that made the function viable,
   // the program is ill-formed.
-  if (Best->Function && Best->Function->isExternC() &&
-      Best->ExplicitCallArguments < Best->Function->getNumParams()) {
-    // Calculate the range of parameters,
-    // default arguments of which made the candidate viable.
-    int FirstDefaultArgIndex = Best->ExplicitCallArguments;
-    int LastDefaultArgIndex = Best->Function->getNumParams() - 1;
-
-    // For each such parameter, collect all redeclarations
-    // that have non-inherited default argument.
-    llvm::SmallDenseMap<int, llvm::TinyPtrVector<ParmVarDecl *>> ParamRedecls(
-        LastDefaultArgIndex - FirstDefaultArgIndex + 1);
-    for (FunctionDecl *Redecl : Best->Function->redecls()) {
-      for (int i = FirstDefaultArgIndex; i <= LastDefaultArgIndex; ++i) {
-        ParmVarDecl *Param = Redecl->getParamDecl(i);
-        if (Param->hasDefaultArg() && !Param->hasInheritedDefaultArg())
-          ParamRedecls[i].push_back(Param);
-      }
-    }
 
-    // Emit the diagnostic if a given parameter has more than one declaration.
-    // MergeCXXFunctionDecl takes care of redeclarations of a default argument
-    // in the same scope, so if we found more than one,
-    // we assume they come from different scopes.
-    for (auto [ParamIndex, Redecls] : ParamRedecls) {
-      assert(!Redecls.empty());
-      if (Redecls.size() == 1)
-        continue;
+  // Calculate the range of parameters,
+  // default arguments of which made the candidate viable.
+  int FirstDefaultArgIndex = NumArgs;
+  int LastDefaultArgIndex = FDecl->getNumParams() - 1;
+
+  // For each such parameter, collect all redeclarations
+  // that have non-inherited default argument.
+  llvm::SmallDenseMap<int, llvm::TinyPtrVector<ParmVarDecl *>> ParamRedecls(
+      LastDefaultArgIndex - FirstDefaultArgIndex + 1);
+  for (FunctionDecl *Redecl : FDecl->redecls()) {
+    for (int i = FirstDefaultArgIndex; i <= LastDefaultArgIndex; ++i) {
+      ParmVarDecl *Param = Redecl->getParamDecl(i);
+      if (Param->hasDefaultArg() && !Param->hasInheritedDefaultArg())
+        ParamRedecls[i].push_back(Param);
+    }
+  }
+
+  // Emit the diagnostic if a given parameter has more than one declaration.
+  // MergeCXXFunctionDecl takes care of redeclarations of a default argument
+  // in the same scope, so if we found more than one,
+  // we assume they come from different scopes.
+  for (auto [ParamIndex, Redecls] : ParamRedecls) {
+    assert(!Redecls.empty());
+    if (Redecls.size() == 1)
+      continue;
 
-      ParmVarDecl *Param = Best->Function->getParamDecl(ParamIndex);
-      if (!Param->getDeclName().isEmpty()) {
-        S.Diag(Loc, diag::err_ovl_ambiguous_default_arg)
-            << 1 << Param->getName();
-      } else
-        S.Diag(Loc, diag::err_ovl_ambiguous_default_arg) << 0;
-      for (ParmVarDecl *Param : Redecls) {
-        S.Diag(Param->getDefaultArg()->getExprLoc(),
-               diag::note_default_argument_declared_here);
-      }
+    ParmVarDecl *Param = FDecl->getParamDecl(ParamIndex);
+    if (!Param->getDeclName().isEmpty()) {
+      Diag(CallLoc, diag::err_ovl_ambiguous_default_arg)
+          << 1 << Param->getName();
+    } else
+      Diag(CallLoc, diag::err_ovl_ambiguous_default_arg) << 0;
+    for (ParmVarDecl *Param : Redecls) {
+      Diag(Param->getDefaultArg()->getExprLoc(),
+           diag::note_default_argument_declared_here);
     }
   }
-
-  return OR_Success;
 }
 
 namespace {
diff --git a/clang/test/CXX/drs/cwg0xx.cpp b/clang/test/CXX/drs/cwg0xx.cpp
index 6b969925c8c191..bc8e9c12a98dfa 100644
--- a/clang/test/CXX/drs/cwg0xx.cpp
+++ b/clang/test/CXX/drs/cwg0xx.cpp
@@ -43,20 +43,25 @@ namespace cwg1 { // cwg1: 20
     //   expected-note@#cwg1-z {{previous definition is here}}
   }
 
-  void i(int = 1);
+  void i(int = 1); // #cwg1-i
   void j() {
-    void i(int = 1);
+    void i(int = 1); // #cwg1-i-redecl
     using cwg1::i;
     i(0);
-    // FIXME: This should be rejected, due to the ambiguous default argument.
     i();
+    // expected-error@-1 {{function call relies on ambiguous default argument}}
+    //   expected-note@#cwg1-i-redecl {{default argument declared here}}
+    //   expected-note@#cwg1-i {{default argument declared here}}
   }
   void k() {
     using cwg1::i;
-    void i(int = 1);
+    void i(int = 1); // #cwg1-i-redecl2
     i(0);
-    // FIXME: This should be rejected, due to the ambiguous default argument.
     i();
+    // expected-error@-1 {{function call relies on ambiguous default argument}}
+    //   expected-note@#cwg1-i-redecl2 {{default argument declared here}}
+    //   expected-note@#cwg1-i-redecl {{default argument declared here}}
+    //   expected-note@#cwg1-i {{default argument declared here}}
   }
 } // namespace cwg1
 
diff --git a/clang/test/CodeGenCXX/default-arguments.cpp 
b/clang/test/CodeGenCXX/default-arguments.cpp
index 2459ef1ad41fcd..002d9fa703ccfe 100644
--- a/clang/test/CodeGenCXX/default-arguments.cpp
+++ b/clang/test/CodeGenCXX/default-arguments.cpp
@@ -74,14 +74,3 @@ void f3() {
  B *bs = new B[2];
  delete bs;
 }
-
-void f4() {
-  void g4(int a, int b = 7);
-  {
-    void g4(int a, int b = 5);
-  }
-  void g4(int a = 5, int b);
-
-  // CHECK: call void @_Z2g4ii(i32 noundef 5, i32 noundef 7)
-  g4();
-}
diff --git a/clang/test/SemaCXX/default-argument-different-scopes.cpp 
b/clang/test/SemaCXX/default-argument-different-scopes.cpp
new file mode 100644
index 00000000000000..f61cc62c117e51
--- /dev/null
+++ b/clang/test/SemaCXX/default-argument-different-scopes.cpp
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
+
+namespace A {
+  extern "C" void f1(...);
+  extern "C" void f2(int, ...);
+  extern "C" void f3(int = 0, ...); // #A-f3
+} // namespace A
+
+namespace B {
+  extern "C" void f1(...);
+  extern "C" void f2(int, ...); // #B-f2
+  extern "C" void f3(int = 0, ...); // #B-f3
+} // namespace B
+
+void f() {
+  using A::f1;
+  using A::f2;
+  using A::f3;
+  using B::f1;
+  using B::f2;
+  using B::f3;
+
+  f1();
+  f1(0);
+  f1(0, 0);
+  f2();
+  // expected-error@-1 {{no matching function for call to 'f2'}}
+  //   expected-note@#B-f2 {{candidate function not viable: requires at least 
1 argument, but 0 were provided}}
+  f2(0);
+  f2(0, 0);
+  f3();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#B-f3 {{default argument declared here}}
+  //   expected-note@#A-f3 {{default argument declared here}}
+  f3(0);
+  f3(0, 0);
+}
+
+#define P_10(x) x, x, x, x, x, x, x, x, x, x,
+#define P_100(x) P_10(x) P_10(x) P_10(x) P_10(x) P_10(x) \
+                 P_10(x) P_10(x) P_10(x) P_10(x) P_10(x)
+#define P_1000(x) P_100(x) P_100(x) P_100(x) P_100(x) P_100(x) \
+                  P_100(x) P_100(x) P_100(x) P_100(x) P_100(x)
+#define P_10000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) \
+                   P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x)
+
+namespace C1 {
+extern "C" int g( 
+  P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) 
P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) int = 0
+  // expected-error@-1 {{too many function parameters; subsequent parameters 
will be ignored}}
+);
+} // namespace C1
+
+using C1::g;
+int h = g();
+
+void i1(int = 2); // #i1
+void i2(int = 2); // #i2
+extern "C" void j1(int = 2); // #j1
+extern "C" void j2(int = 2); // #j2
+
+void f2() {
+  void i1(int = 3); // #i1-redecl
+  extern void i2(int = 3); // #i2-redecl
+  void j1(int = 3); // #j1-redecl
+  extern void j2(int = 3); // #j2-redecl
+
+  i1();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#i1-redecl {{default argument declared here}}
+  //   expected-note@#i1 {{default argument declared here}}
+  ::i1();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#i1 {{default argument declared here}}
+  //   expected-note@#i1-redecl {{default argument declared here}}
+  i2();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#i2-redecl {{default argument declared here}}
+  //   expected-note@#i2 {{default argument declared here}}
+  ::i2();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#i2 {{default argument declared here}}
+  //   expected-note@#i2-redecl {{default argument declared here}}
+  j1();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#j1-redecl {{default argument declared here}}
+  //   expected-note@#j1 {{default argument declared here}}
+  ::j1();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#j1 {{default argument declared here}}
+  //   expected-note@#j1-redecl {{default argument declared here}}
+  j2();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#j2-redecl {{default argument declared here}}
+  //   expected-note@#j2 {{default argument declared here}}
+  ::j2();
+  // expected-error@-1 {{function call relies on ambiguous default argument}}
+  //   expected-note@#j2 {{default argument declared here}}
+  //   expected-note@#j2-redecl {{default argument declared here}}
+}
diff --git a/clang/test/SemaCXX/default-argument-extern-c.cpp 
b/clang/test/SemaCXX/default-argument-extern-c.cpp
deleted file mode 100644
index 4175b0d231ca51..00000000000000
--- a/clang/test/SemaCXX/default-argument-extern-c.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2c %s
-
-namespace A {
-  extern "C" void f1(...);
-  extern "C" void f2(int, ...);
-  extern "C" void f3(int = 0, ...); // #A-f3
-} // namespace A
-
-namespace B {
-  extern "C" void f1(...);
-  extern "C" void f2(int, ...); // #B-f2
-  extern "C" void f3(int = 0, ...); // #B-f3
-} // namespace B
-
-void f() {
-  using A::f1;
-  using A::f2;
-  using A::f3;
-  using B::f1;
-  using B::f2;
-  using B::f3;
-
-  f1();
-  f1(0);
-  f1(0, 0);
-  f2();
-  // expected-error@-1 {{no matching function for call to 'f2'}}
-  //   expected-note@#B-f2 {{candidate function not viable: requires at least 
1 argument, but 0 were provided}}
-  f2(0);
-  f2(0, 0);
-  f3();
-  // expected-error@-1 {{function call relies on ambiguous default argument}}
-  //   expected-note@#B-f3 {{default argument declared here}}
-  //   expected-note@#A-f3 {{default argument declared here}}
-  f3(0);
-  f3(0, 0);
-}
-
-#define P_10(x) x, x, x, x, x, x, x, x, x, x,
-#define P_100(x) P_10(x) P_10(x) P_10(x) P_10(x) P_10(x) \
-                 P_10(x) P_10(x) P_10(x) P_10(x) P_10(x)
-#define P_1000(x) P_100(x) P_100(x) P_100(x) P_100(x) P_100(x) \
-                  P_100(x) P_100(x) P_100(x) P_100(x) P_100(x)
-#define P_10000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) \
-                   P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x)
-
-namespace C1 {
-extern "C" int g( 
-  P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) 
P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) int = 0
-  // expected-error@-1 {{too many function parameters; subsequent parameters 
will be ignored}}
-);
-} // namespace C1
-
-using C1::g;
-int h = g();
diff --git a/clang/test/SemaCXX/default1.cpp b/clang/test/SemaCXX/default1.cpp
index 8345b2433a3fe1..675a3a61e4ca79 100644
--- a/clang/test/SemaCXX/default1.cpp
+++ b/clang/test/SemaCXX/default1.cpp
@@ -41,11 +41,14 @@ void kk(Y = 17); // expected-error{{no viable conversion}} \
 // expected-note{{passing argument to parameter here}}
 
 int l () {
-  int m(int i, int j, int k = 3);
+  int m(int i, int j, int k = 3); // #m
   if (1)
   {
-    int m(int i, int j = 2, int k = 4);
+    int m(int i, int j = 2, int k = 4); // #m-redecl
     m(8);
+    // expected-error@-1 {{function call relies on ambiguous default argument 
for parameter 'k'}}
+    //   expected-note@#m-redecl {{default argument declared here}}
+    //   expected-note@#m {{default argument declared here}}
   }
   return 0;
 }

>From 5a76dc9eba67f45f87595eb86f85872ccf066923 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Tue, 28 Jan 2025 17:01:22 +0400
Subject: [PATCH 04/13] Fix type in the reference to the Standard

---
 clang/lib/Sema/SemaDeclCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 28cd94fb855db2..8fb6e378ead7ba 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -480,7 +480,7 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
       //
       // extern "C" functions can have default arguments across different
       // scopes, but diagnosing that early would reject well-formed code
-      // (_N5001_.[over.best.match]/4.) Instead, they are checked
+      // (_N5001_.[over.match.best]/4.) Instead, they are checked
       // in BestViableFunction after the best viable function has been 
selected.
       continue;
     }

>From ce204e4905bec9c042d4b355eb431200e8788a52 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Tue, 28 Jan 2025 17:08:49 +0400
Subject: [PATCH 05/13] Leave a comment in BestViableFunction

---
 clang/lib/Sema/SemaOverload.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index cf4ac6a0b9af44..26a4d5a822e3d8 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10951,6 +10951,10 @@ OverloadCandidateSet::BestViableFunction(Sema &S, 
SourceLocation Loc,
     S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
                                                     EquivalentCands);
 
+  // [over.match.best]/4 is checked for in Sema::ConvertArgumentsForCall,
+  // because not every function call goes through our overload resolution
+  // machinery, even if the Standard says it supposed to.
+
   return OR_Success;
 }
 

>From f47e8cc9a659743abb34e91ee53fa5d0af4cb416 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 00:17:30 +0400
Subject: [PATCH 06/13] Handle friend declarations

---
 clang/lib/Sema/SemaDeclCXX.cpp                | 21 +++++--
 clang/test/CXX/drs/cwg0xx.cpp                 |  6 +-
 clang/test/CXX/drs/cwg1xx.cpp                 |  1 +
 .../default-argument-different-scopes.cpp     | 62 +++++++++++++++++++
 4 files changed, 81 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 8fb6e378ead7ba..b13362426f192e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -454,11 +454,13 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
   bool Invalid = false;
 
   // The declaration context corresponding to the scope is the semantic
-  // parent, unless this is a local function declaration, in which case
-  // it is that surrounding function.
-  DeclContext *ScopeDC = New->isLocalExternDecl()
-                             ? New->getLexicalDeclContext()
-                             : New->getDeclContext();
+  // parent, unless this is a local function declaration
+  // or a friend declaration, in which case it is that surrounding function.
+  DeclContext *ScopeDC =
+      New->isLocalExternDecl() ||
+              New->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)
+          ? New->getLexicalDeclContext()
+          : New->getDeclContext();
 
   // Find the previous declaration for the purpose of default arguments.
   FunctionDecl *PrevForDefaultArgs = Old;
@@ -493,6 +495,15 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
       continue;
     }
 
+    if (PrevForDefaultArgs->getLexicalDeclContext()->getPrimaryContext() !=
+            ScopeDC->getPrimaryContext() &&
+        !New->isCXXClassMember()) {
+      // If previous declaration is lexically in a different scope,
+      // we don't inherit its default arguments, except for out-of-line
+      // declarations of member functions.
+      continue;
+    }
+
     // We found the right previous declaration.
     break;
   }
diff --git a/clang/test/CXX/drs/cwg0xx.cpp b/clang/test/CXX/drs/cwg0xx.cpp
index bc8e9c12a98dfa..4d45898a397766 100644
--- a/clang/test/CXX/drs/cwg0xx.cpp
+++ b/clang/test/CXX/drs/cwg0xx.cpp
@@ -36,11 +36,9 @@ namespace cwg1 { // cwg1: 20
   namespace X {
     void z(int);
   }
-  void X::z(int = 1) {} // #cwg1-z
+  void X::z(int = 1) {}
   namespace X {
-    void z(int = 1);
-    // expected-error@-1 {{redefinition of default argument}}
-    //   expected-note@#cwg1-z {{previous definition is here}}
+    void z(int = 1); // OK, namespace X has a distinct set of default arguments
   }
 
   void i(int = 1); // #cwg1-i
diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp
index 15bcc20b7fa2a9..f6aa0b3ffb35c6 100644
--- a/clang/test/CXX/drs/cwg1xx.cpp
+++ b/clang/test/CXX/drs/cwg1xx.cpp
@@ -518,6 +518,7 @@ namespace cwg136 { // cwg136: 3.4
     friend void f(int, int = 0, int);
     // expected-error@-1 {{friend declaration specifying a default argument 
must be the only declaration}}
     //   expected-note@#cwg136-f {{previous declaration is here}}
+    // expected-error@-3 {{missing default argument on parameter}}
     friend void g(int, int, int = 0);
     // expected-error@-1 {{friend declaration specifying a default argument 
must be the only declaration}}
     //   expected-note@#cwg136-g {{previous declaration is here}}
diff --git a/clang/test/SemaCXX/default-argument-different-scopes.cpp 
b/clang/test/SemaCXX/default-argument-different-scopes.cpp
index f61cc62c117e51..e78f120fea0485 100644
--- a/clang/test/SemaCXX/default-argument-different-scopes.cpp
+++ b/clang/test/SemaCXX/default-argument-different-scopes.cpp
@@ -100,3 +100,65 @@ void f2() {
   //   expected-note@#j2 {{default argument declared here}}
   //   expected-note@#j2-redecl {{default argument declared here}}
 }
+
+// In 'k' group of tests, no redefinition of default arguments occur,
+// because sets of default arguments are associated with lexical scopes
+// of function declarations.
+
+void k1(int); // #k1
+void k2(int = 2);
+void k3(int = 3); // #k3
+
+struct K {
+  friend void k1(int = 1) {}
+  // expected-error@-1 {{friend declaration specifying a default argument must 
be the only declaration}}
+  //   expected-note@#k1 {{previous declaration is here}}
+  friend void k2(int) {}
+  friend void k3(int = 3) {}
+  // expected-error@-1 {{friend declaration specifying a default argument must 
be the only declaration}}
+  //   expected-note@#k3 {{previous declaration is here}}
+
+  friend void k4(int = 4) {} // #k4
+  friend void k5(int) {}
+  friend void k6(int = 6) {} // #k6
+};
+
+void k4(int);
+// expected-error@-1 {{friend declaration specifying a default argument must 
be the only declaration}}
+//   expected-note@#k4 {{previous declaration is here}}
+void k5(int = 5);
+void k6(int = 6);
+// expected-error@-1 {{friend declaration specifying a default argument must 
be the only declaration}}
+//   expected-note@#k6 {{previous declaration is here}}
+
+struct L {
+  void l1(int);
+  void l2(int = 2);
+  void l3(int = 3); // #l3
+
+  template <typename>
+  void l4(int); // #l4
+  template <typename>
+  void l5(int = 5);
+  template <typename>
+  void l6(int = 6); // #l6
+};
+
+void L::l1(int = 1) {}
+void L::l2(int) {}
+void L::l3(int = 3) {}
+// expected-error@-1 {{redefinition of default argument}}
+//   expected-note@#l3 {{previous definition is here}}
+
+template <typename>
+void L::l4(int = 4) {}
+// expected-error@-1 {{default arguments cannot be added to a function 
template that has already been declared}}
+//   expected-note@#l4 {{previous template declaration is here}}
+
+template <typename>
+void L::l5(int) {}
+
+template <typename>
+void L::l6(int = 6) {}
+// expected-error@-1 {{redefinition of default argument}}
+//   expected-note@#l6 {{previous definition is here}}

>From bfa5851a318826018f8c592c88e853726383dcbe Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 00:22:51 +0400
Subject: [PATCH 07/13] Update release notes

---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 18d6944083cb27..ad2d7f02a59aad 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -384,7 +384,7 @@ Resolutions to C++ Defect Reports
   (`CWG2521: User-defined literals and reserved identifiers 
<https://cplusplus.github.io/CWG/issues/2521.html>`_).
 
 - Clang now diagnoses ambiguous default arguments declared in different scopes
-  when calling an ``extern "C"`` function, implementing [over.best.match] p4.
+  when calling functions, implementing [over.best.match] p4.
   (`CWG1: What if two using-declarations refer to the same function but the 
declarations introduce different default-arguments? 
<https://cplusplus.github.io/CWG/issues/1.html>`_,
   `CWG418: Imperfect wording on error on multiple default arguments on a 
called function <https://cplusplus.github.io/CWG/issues/418.html>`_)
 

>From 084fc667bb869d666b623b933b8aa613ab941ccb Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 00:23:41 +0400
Subject: [PATCH 08/13] Fix stable name in release notes

---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ad2d7f02a59aad..f2c722ce92841b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -384,7 +384,7 @@ Resolutions to C++ Defect Reports
   (`CWG2521: User-defined literals and reserved identifiers 
<https://cplusplus.github.io/CWG/issues/2521.html>`_).
 
 - Clang now diagnoses ambiguous default arguments declared in different scopes
-  when calling functions, implementing [over.best.match] p4.
+  when calling functions, implementing [over.match.best] p4.
   (`CWG1: What if two using-declarations refer to the same function but the 
declarations introduce different default-arguments? 
<https://cplusplus.github.io/CWG/issues/1.html>`_,
   `CWG418: Imperfect wording on error on multiple default arguments on a 
called function <https://cplusplus.github.io/CWG/issues/418.html>`_)
 

>From e7411f8b5bc7fc2e69c39ca3c375b9a1ae6523d8 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 01:02:44 +0400
Subject: [PATCH 09/13] Move the new check function to `SemaExpr.cpp` where
 it's now used

---
 clang/include/clang/Sema/Sema.h | 10 ------
 clang/lib/Sema/SemaExpr.cpp     | 58 ++++++++++++++++++++++++++++++++-
 clang/lib/Sema/SemaOverload.cpp | 48 ---------------------------
 3 files changed, 57 insertions(+), 59 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 473315ffd50f8f..528304409b8092 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10389,16 +10389,6 @@ class Sema final : public SemaBase {
                                          bool Complain = false,
                                          SourceLocation Loc = 
SourceLocation());
 
-  /// @brief Checks that each default argument needed to make the call
-  /// is defined only once, implementing [over.match.best]/4 rule.
-  ///
-  /// @param FDecl Function declaration selected for the call
-  /// @param NumArgs Number of argument explicitly specified in the call
-  /// expression
-  /// @param CallLoc Source location of the call expression
-  void checkDefaultArgumentsAcrossScopes(FunctionDecl *FDecl, int NumArgs,
-                                         SourceLocation CallLoc);
-
   // [PossiblyAFunctionType]  -->   [Return]
   // NonFunctionType --> NonFunctionType
   // R (A) --> R(A)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5dfcc04527b9f3..742f3452327687 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5810,6 +5810,62 @@ static bool 
isParenthetizedAndQualifiedAddressOfExpr(Expr *Fn) {
   return false;
 }
 
+/// @brief Checks that each default argument needed to make the call
+/// is defined only once, implementing [over.match.best]/4 rule.
+///
+/// @param FDecl Function declaration selected for the call
+/// @param NumArgs Number of argument explicitly specified in the call
+/// expression
+/// @param CallLoc Source location of the call expression
+static void checkDefaultArgumentsAcrossScopes(Sema &S, FunctionDecl *FDecl,
+                                              int NumArgs,
+                                              SourceLocation CallLoc) {
+  // [over.match.best]/4:
+  // If the best viable function resolves to a function
+  // for which multiple declarations were found,
+  // and if any two of these declarations inhabit different scopes
+  // and specify a default argument that made the function viable,
+  // the program is ill-formed.
+
+  // Calculate the range of parameters,
+  // default arguments of which made the candidate viable.
+  int FirstDefaultArgIndex = NumArgs;
+  int LastDefaultArgIndex = FDecl->getNumParams() - 1;
+
+  // For each such parameter, collect all redeclarations
+  // that have non-inherited default argument.
+  llvm::SmallDenseMap<int, llvm::TinyPtrVector<ParmVarDecl *>> ParamRedecls(
+      LastDefaultArgIndex - FirstDefaultArgIndex + 1);
+  for (FunctionDecl *Redecl : FDecl->redecls()) {
+    for (int i = FirstDefaultArgIndex; i <= LastDefaultArgIndex; ++i) {
+      ParmVarDecl *Param = Redecl->getParamDecl(i);
+      if (Param->hasDefaultArg() && !Param->hasInheritedDefaultArg())
+        ParamRedecls[i].push_back(Param);
+    }
+  }
+
+  // Emit the diagnostic if a given parameter has more than one declaration.
+  // MergeCXXFunctionDecl takes care of redeclarations of a default argument
+  // in the same scope, so if we found more than one,
+  // we assume they come from different scopes.
+  for (auto [ParamIndex, Redecls] : ParamRedecls) {
+    assert(!Redecls.empty());
+    if (Redecls.size() == 1)
+      continue;
+
+    ParmVarDecl *Param = FDecl->getParamDecl(ParamIndex);
+    if (!Param->getDeclName().isEmpty()) {
+      S.Diag(CallLoc, diag::err_ovl_ambiguous_default_arg)
+          << 1 << Param->getName();
+    } else
+      S.Diag(CallLoc, diag::err_ovl_ambiguous_default_arg) << 0;
+    for (ParmVarDecl *Param : Redecls) {
+      S.Diag(Param->getDefaultArg()->getExprLoc(),
+             diag::note_default_argument_declared_here);
+    }
+  }
+}
+
 bool
 Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
                               FunctionDecl *FDecl,
@@ -5888,7 +5944,7 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
         std::any_of(
             FDecl->redecls_begin(), FDecl->redecls_end(),
             [](FunctionDecl *Redecl) { return Redecl->isLocalExternDecl(); })) 
{
-      checkDefaultArgumentsAcrossScopes(FDecl, Args.size(),
+      checkDefaultArgumentsAcrossScopes(*this, FDecl, Args.size(),
                                         Call->getBeginLoc());
     }
   }
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 26a4d5a822e3d8..0b616aa07528b7 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10958,54 +10958,6 @@ OverloadCandidateSet::BestViableFunction(Sema &S, 
SourceLocation Loc,
   return OR_Success;
 }
 
-void Sema::checkDefaultArgumentsAcrossScopes(FunctionDecl *FDecl, int NumArgs,
-                                             SourceLocation CallLoc) {
-  // [over.match.best]/4:
-  // If the best viable function resolves to a function
-  // for which multiple declarations were found,
-  // and if any two of these declarations inhabit different scopes
-  // and specify a default argument that made the function viable,
-  // the program is ill-formed.
-
-  // Calculate the range of parameters,
-  // default arguments of which made the candidate viable.
-  int FirstDefaultArgIndex = NumArgs;
-  int LastDefaultArgIndex = FDecl->getNumParams() - 1;
-
-  // For each such parameter, collect all redeclarations
-  // that have non-inherited default argument.
-  llvm::SmallDenseMap<int, llvm::TinyPtrVector<ParmVarDecl *>> ParamRedecls(
-      LastDefaultArgIndex - FirstDefaultArgIndex + 1);
-  for (FunctionDecl *Redecl : FDecl->redecls()) {
-    for (int i = FirstDefaultArgIndex; i <= LastDefaultArgIndex; ++i) {
-      ParmVarDecl *Param = Redecl->getParamDecl(i);
-      if (Param->hasDefaultArg() && !Param->hasInheritedDefaultArg())
-        ParamRedecls[i].push_back(Param);
-    }
-  }
-
-  // Emit the diagnostic if a given parameter has more than one declaration.
-  // MergeCXXFunctionDecl takes care of redeclarations of a default argument
-  // in the same scope, so if we found more than one,
-  // we assume they come from different scopes.
-  for (auto [ParamIndex, Redecls] : ParamRedecls) {
-    assert(!Redecls.empty());
-    if (Redecls.size() == 1)
-      continue;
-
-    ParmVarDecl *Param = FDecl->getParamDecl(ParamIndex);
-    if (!Param->getDeclName().isEmpty()) {
-      Diag(CallLoc, diag::err_ovl_ambiguous_default_arg)
-          << 1 << Param->getName();
-    } else
-      Diag(CallLoc, diag::err_ovl_ambiguous_default_arg) << 0;
-    for (ParmVarDecl *Param : Redecls) {
-      Diag(Param->getDefaultArg()->getExprLoc(),
-           diag::note_default_argument_declared_here);
-    }
-  }
-}
-
 namespace {
 
 enum OverloadCandidateKind {

>From b250d9020af1b740cbf47a83eee30782187f50a4 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 01:09:10 +0400
Subject: [PATCH 10/13] Mark DRs are available in Clang 21

---
 clang/test/CXX/drs/cwg0xx.cpp | 2 +-
 clang/test/CXX/drs/cwg4xx.cpp | 2 +-
 clang/www/cxx_dr_status.html  | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/test/CXX/drs/cwg0xx.cpp b/clang/test/CXX/drs/cwg0xx.cpp
index 9fdfa36f891d50..f86431e21a7669 100644
--- a/clang/test/CXX/drs/cwg0xx.cpp
+++ b/clang/test/CXX/drs/cwg0xx.cpp
@@ -11,7 +11,7 @@
 // cxx98-error@-1 {{variadic macros are a C99 feature}}
 #endif
 
-namespace cwg1 { // cwg1: 20
+namespace cwg1 { // cwg1: 21
   namespace X { extern "C" void cwg1_f(int a = 1); } // #cwg1-X
   namespace Y { extern "C" void cwg1_f(int a = 1); } // #cwg1-Y
   using X::cwg1_f; using Y::cwg1_f;
diff --git a/clang/test/CXX/drs/cwg4xx.cpp b/clang/test/CXX/drs/cwg4xx.cpp
index 782ed70c5741b9..5f1aac1bdfe403 100644
--- a/clang/test/CXX/drs/cwg4xx.cpp
+++ b/clang/test/CXX/drs/cwg4xx.cpp
@@ -369,7 +369,7 @@ namespace cwg417 { // cwg417: no
   }
 } // namespace cwg417
 
-namespace cwg418 { // cwg418: 20
+namespace cwg418 { // cwg418: 21
 namespace example1 {
 void f1(int, int = 0);
 void f1(int = 0, int);
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index d9eb8c26317f3b..d8fb845b5bebae 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -51,7 +51,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/1.html";>1</a></td>
     <td>TC1</td>
     <td>What if two using-declarations refer to the same function but the 
declarations introduce different default-arguments?</td>
-    <td class="unreleased" align="center">Clang 20</td>
+    <td class="unreleased" align="center">Clang 21</td>
   </tr>
   <tr class="open" id="2">
     <td><a href="https://cplusplus.github.io/CWG/issues/2.html";>2</a></td>
@@ -2555,7 +2555,7 @@ <h2 id="cxxdr">C++ defect report implementation 
status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/418.html";>418</a></td>
     <td>CD6</td>
     <td>Imperfect wording on error on multiple default arguments on a called 
function</td>
-    <td class="unreleased" align="center">Clang 20</td>
+    <td class="unreleased" align="center">Clang 21</td>
   </tr>
   <tr class="open" id="419">
     <td><a href="https://cplusplus.github.io/CWG/issues/419.html";>419</a></td>

>From afdf43bfaf1c78a309741e83b1c40cb64ffbaee6 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 01:14:55 +0400
Subject: [PATCH 11/13] Update comment in MergeCXXFunctionDecl

---
 clang/lib/Sema/SemaDeclCXX.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 40989ef50eca78..b0e946abe7e9c4 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -479,11 +479,6 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
       // Ignore default arguments of old decl if they are not in
       // the same scope and this is not an out-of-line definition of
       // a member function.
-      //
-      // extern "C" functions can have default arguments across different
-      // scopes, but diagnosing that early would reject well-formed code
-      // (_N5001_.[over.match.best]/4.) Instead, they are checked
-      // in BestViableFunction after the best viable function has been 
selected.
       continue;
     }
 
@@ -501,6 +496,12 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
       // If previous declaration is lexically in a different scope,
       // we don't inherit its default arguments, except for out-of-line
       // declarations of member functions.
+      //
+      // extern "C" and local functions can have default arguments across
+      // different scopes, but diagnosing that early would reject well-formed
+      // code (_N5001_.[over.match.best]/4.) Instead, they are checked
+      // in ConvertArgumentsForCall, after the best viable function has been
+      // selected.
       continue;
     }
 

>From 344cd4c7248c933850cc509a37a5d516efc16c96 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 01:32:53 +0400
Subject: [PATCH 12/13] Move the stress test around

---
 .../test/Parser/function-parameter-limit.cpp  | 20 +++++++++++++++++++
 .../default-argument-different-scopes.cpp     | 18 -----------------
 2 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/clang/test/Parser/function-parameter-limit.cpp 
b/clang/test/Parser/function-parameter-limit.cpp
index 29f5dde294715c..b543f6a7ac8489 100644
--- a/clang/test/Parser/function-parameter-limit.cpp
+++ b/clang/test/Parser/function-parameter-limit.cpp
@@ -27,3 +27,23 @@ extern double(*func2)(
   P_10000(int u)
   P_10000(int v) // expected-error {{too many function parameters; subsequent 
parameters will be ignored}}
   int w);
+
+#define PD_10(x) x, x, x, x, x, x, x, x, x, x,
+#define PD_100(x) PD_10(x) PD_10(x) PD_10(x) PD_10(x) PD_10(x) \
+                  PD_10(x) PD_10(x) PD_10(x) PD_10(x) PD_10(x)
+#define PD_1000(x) PD_100(x) PD_100(x) PD_100(x) PD_100(x) PD_100(x) \
+                   PD_100(x) PD_100(x) PD_100(x) PD_100(x) PD_100(x)
+#define PD_10000(x) PD_1000(x) PD_1000(x) PD_1000(x) PD_1000(x) PD_1000(x) \
+                    PD_1000(x) PD_1000(x) PD_1000(x) PD_1000(x) PD_1000(x)
+
+extern "C" int func3( 
+  PD_10000(int = 0)
+  PD_10000(int = 0)
+  PD_10000(int = 0)
+  PD_10000(int = 0)
+  PD_10000(int = 0)
+  PD_10000(int = 0)
+  PD_10000(int = 0) // expected-error {{too many function parameters; 
subsequent parameters will be ignored}} 
+  int = 0);
+
+int h = func3();
diff --git a/clang/test/SemaCXX/default-argument-different-scopes.cpp 
b/clang/test/SemaCXX/default-argument-different-scopes.cpp
index e78f120fea0485..cb7addedb7ec20 100644
--- a/clang/test/SemaCXX/default-argument-different-scopes.cpp
+++ b/clang/test/SemaCXX/default-argument-different-scopes.cpp
@@ -38,24 +38,6 @@ void f() {
   f3(0, 0);
 }
 
-#define P_10(x) x, x, x, x, x, x, x, x, x, x,
-#define P_100(x) P_10(x) P_10(x) P_10(x) P_10(x) P_10(x) \
-                 P_10(x) P_10(x) P_10(x) P_10(x) P_10(x)
-#define P_1000(x) P_100(x) P_100(x) P_100(x) P_100(x) P_100(x) \
-                  P_100(x) P_100(x) P_100(x) P_100(x) P_100(x)
-#define P_10000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x) \
-                   P_1000(x) P_1000(x) P_1000(x) P_1000(x) P_1000(x)
-
-namespace C1 {
-extern "C" int g( 
-  P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) 
P_10000(int = 0) P_10000(int = 0) P_10000(int = 0) int = 0
-  // expected-error@-1 {{too many function parameters; subsequent parameters 
will be ignored}}
-);
-} // namespace C1
-
-using C1::g;
-int h = g();
-
 void i1(int = 2); // #i1
 void i2(int = 2); // #i2
 extern "C" void j1(int = 2); // #j1

>From 0e24e378a7ca98064137eb49e85d5850d0638705 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladis...@gmail.com>
Date: Wed, 29 Jan 2025 01:37:35 +0400
Subject: [PATCH 13/13] I love our coding standard w.r.t. single-line if
 statements

---
 clang/lib/Sema/SemaDeclCXX.cpp | 3 +--
 clang/lib/Sema/SemaExpr.cpp    | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index b0e946abe7e9c4..75b66d8d6298d9 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -492,7 +492,7 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
 
     if (PrevForDefaultArgs->getLexicalDeclContext()->getPrimaryContext() !=
             ScopeDC->getPrimaryContext() &&
-        !New->isCXXClassMember()) {
+        !New->isCXXClassMember())
       // If previous declaration is lexically in a different scope,
       // we don't inherit its default arguments, except for out-of-line
       // declarations of member functions.
@@ -503,7 +503,6 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
       // in ConvertArgumentsForCall, after the best viable function has been
       // selected.
       continue;
-    }
 
     // We found the right previous declaration.
     break;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 742f3452327687..b177811ef19d03 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5943,10 +5943,9 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
     if (FDecl->isExternC() ||
         std::any_of(
             FDecl->redecls_begin(), FDecl->redecls_end(),
-            [](FunctionDecl *Redecl) { return Redecl->isLocalExternDecl(); })) 
{
+            [](FunctionDecl *Redecl) { return Redecl->isLocalExternDecl(); }))
       checkDefaultArgumentsAcrossScopes(*this, FDecl, Args.size(),
                                         Call->getBeginLoc());
-    }
   }
 
   // If too many are passed and not variadic, error on the extras and drop

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to