diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5780f5d61d579..83e76519cd3c9 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -83,10 +83,10 @@ C++17 Feature Support Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- The flag `-frelaxed-template-template-args` - and its negation have been removed, having been deprecated since the previous - two releases. The improvements to template template parameter matching implemented - in the previous release, as described in P3310 and P3579, made this flag unnecessary. +- Clang now diagnoses ambiguous default arguments declared in different scopes + 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? `_, + `CWG418: Imperfect wording on error on multiple default arguments on a called function `_) C Language Changes ------------------ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index feef50812eca9..39f05fb083ed1 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5142,6 +5142,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 default argument that has multiple " + "definitions">; + } // let Deferrable // C++11 Literal Operators diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index c03ec00d03dc5..10a02864f76e0 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -901,6 +901,13 @@ class Sema; LLVM_PREFERRED_TYPE(bool) unsigned Viable : 1; + /// Whether this candidate wouldn't be viable without default arguments. + /// This is needed to implement the special provision about ambiguous + /// default arguments of the best viable function described in + /// [over.match.best]/4. + LLVM_PREFERRED_TYPE(bool) + unsigned ViableByDefaultArgument : 1; + /// Whether this candidate is the best viable function, or tied for being /// the best viable function. /// @@ -948,7 +955,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 { @@ -1176,11 +1184,38 @@ class Sema; /// Whether diagnostics should be deferred. bool shouldDeferDiags(Sema &S, ArrayRef Args, SourceLocation OpLoc); - /// Determine when this overload candidate will be new to the - /// overload set. + /// Determine when this overload candidate will be new to the overload set. + /// Typically the uniqueness is determined by canonical declaration + /// (i.e. semantic entity), but it's aware of a special case of default + /// arguments in redeclarations spanning across different lexical scopes. + /// See also eliminateDuplicatesWithUnusedDefaultArgs(). bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO = OverloadCandidateParamOrder::Normal) { uintptr_t Key = reinterpret_cast(F->getCanonicalDecl()); + + // There is one special case when we can't rely on just semantic + // properties of the function (i.e. entity in the Standard sense), + // which is default arguments defined in redeclarations across + // different scopes, because sets of default arguments are associated + // with lexical scope of function declaration. Which means single + // function entity can have more than one set of default arguments, + // and which of those sets are considered depends solely on what + // name lookup has found for each individual call expression. + // See [over.match.best]/4 for more details. + // In such case, we use (lexical) function declaration to determine + // uniqueness of overload candidate, instead of (semantic) entity + // it represents. This would lead to duplicates, which will be + // eliminated by eliminateDuplicatesWithUnusedDefaultArgs() + // after every candidate will be considered. + if (const auto *FD = dyn_cast(F); + FD && FD->getLexicalDeclContext() != + F->getCanonicalDecl()->getLexicalDeclContext()) { + auto HasDefaultArg = [](const ParmVarDecl *PDecl) { + return PDecl->hasDefaultArg(); + }; + if (llvm::any_of(FD->parameters(), HasDefaultArg)) + Key = reinterpret_cast(FD); + } Key |= static_cast(PO); return Functions.insert(Key).second; } @@ -1194,6 +1229,14 @@ class Sema; /// Clear out all of the candidates. void clear(CandidateSetKind CSK); + /// Clean up duplicates left by isNewCandidate() + /// (see comment in its implementation for details.) + /// When this function is called, we should know which of duplicated + /// candidates are viable because of their default arguments, and those + /// are the only duplicates we are interested in during subsequent + /// selection of the best viable function. + void eliminateDuplicatesWithUnusedDefaultArgs(); + using iterator = SmallVectorImpl::iterator; iterator begin() { return Candidates.begin(); } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 610207cf8b9a4..3b7a6f6a40e50 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1883,6 +1883,33 @@ bool NamedDecl::declarationReplaces(const NamedDecl *OldD, cast(OldD)->getQualifier()); } + { + // When default arguments across redeclarations in different lexical scopes + // are involved, we can't let one UsingShadowDecl to replace another based + // on the fact that they refer to the same canonical function. + const auto *OldShadowD = dyn_cast(OldD); + const auto *NewShadowD = dyn_cast(this); + if (OldShadowD && NewShadowD && + isa(OldShadowD->getTargetDecl()) && + isa(NewShadowD->getTargetDecl())) { + const auto *OldFDecl = cast(OldShadowD->getTargetDecl()); + const auto *NewFDecl = cast(NewShadowD->getTargetDecl()); + const DeclContext *OldDeclCtx = OldFDecl->getLexicalDeclContext() + ->getNonTransparentContext() + ->getPrimaryContext(); + const DeclContext *NewDeclCtx = NewFDecl->getLexicalDeclContext() + ->getNonTransparentContext() + ->getPrimaryContext(); + auto hasDefaultArg = [](const ParmVarDecl *PDecl) { + return PDecl->hasDefaultArg(); + }; + if (OldDeclCtx != NewDeclCtx && + llvm::any_of(OldFDecl->parameters(), hasDefaultArg) && + llvm::any_of(NewFDecl->parameters(), hasDefaultArg)) + return false; + } + } + if (isRedeclarable(getKind())) { if (getCanonicalDecl() != OldD->getCanonicalDecl()) return false; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 664d48ccbc382..8eefcb956d6b2 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; @@ -488,6 +490,22 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, continue; } + if (PrevForDefaultArgs->getLexicalDeclContext() + ->getNonTransparentContext() + ->getPrimaryContext() != + ScopeDC->getNonTransparentContext()->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. + // + // extern "C" and local functions can have default arguments across + // different scopes, but diagnosing that early would reject well-formed + // code (C++2c [over.match.best]/4.) Instead, this check is deferred to + // overload resolution. See handling of ambiguous overload resolution + // result in FinishOverloadedCallExpr(). + continue; + // We found the right previous declaration. break; } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0f5b7426e743e..25d9b27375935 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -518,8 +518,28 @@ void LookupResult::resolveKind() { llvm::BitVector RemovedDecls(N); for (unsigned I = 0; I < N; I++) { - const NamedDecl *D = Decls[I]->getUnderlyingDecl(); - D = cast(D->getCanonicalDecl()); + const NamedDecl *NonCanonicalD = Decls[I]->getUnderlyingDecl(); + const NamedDecl *D = cast(NonCanonicalD->getCanonicalDecl()); + + // When a function has redeclarations across different lexical scopes, + // and at least some of them have default arguments, we end up with + // multiple sets of default argument. Canonical declaration can't capture + // that, so we use non-canonical declarations instead. Overload resolution + // machinery is prepared to deal with that, see + // OverloadCandidateSet::isNewCandidate() and + // OverloadCandidateSet::eliminateDuplicatesWithUnusedDefaultArgs(). + const DeclContext *DCtx = D->getLexicalDeclContext() + ->getNonTransparentContext() + ->getPrimaryContext(); + if (const auto *FD = dyn_cast(NonCanonicalD); + FD && DCtx != FD->getLexicalDeclContext() + ->getNonTransparentContext() + ->getPrimaryContext()) { + if (llvm::any_of(FD->parameters(), [](const ParmVarDecl *PDecl) { + return PDecl->hasDefaultArg(); + })) + D = NonCanonicalD; + } // Ignore an invalid declaration unless it's the only one left. // Also ignore HLSLBufferDecl which not have name conflict with other Decls. @@ -551,6 +571,8 @@ void LookupResult::resolveKind() { continue; } + // If this declaration is not the first declaration of an entity, + // this variable holds the index of the declaration we saw. std::optional ExistingI; // Redeclarations of types via typedef can occur both within a scope diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 8d5b5ac190b5b..cb640cec4a749 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1125,6 +1125,15 @@ void OverloadCandidateSet::clear(CandidateSetKind CSK) { Kind = CSK; } +void OverloadCandidateSet::eliminateDuplicatesWithUnusedDefaultArgs() { + llvm::SmallPtrSet UniqueEntities; + llvm::erase_if(Candidates, [&](const OverloadCandidate &Candidate) { + auto [It, New] = + UniqueEntities.insert(Candidate.Function->getCanonicalDecl()); + return !(New || Candidate.ViableByDefaultArgument); + }); +} + namespace { class UnbridgedCastsSet { struct Entry { @@ -6986,6 +6995,7 @@ void Sema::AddOverloadCandidate( Candidate.FoundDecl = FoundDecl; Candidate.Function = Function; Candidate.Viable = true; + Candidate.ViableByDefaultArgument = false; Candidate.RewriteKind = CandidateSet.getRewriteInfo().getRewriteKind(Function, PO); Candidate.IsADLCandidate = llvm::to_underlying(IsADLCandidate); @@ -7113,6 +7123,10 @@ void Sema::AddOverloadCandidate( return; } + if (Args.size() < Function->getNumParams()) { + Candidate.ViableByDefaultArgument = true; + } + // (CUDA B.1): Check for invalid calls between targets. if (getLangOpts().CUDA) { const FunctionDecl *Caller = getCurFunctionDecl(/*AllowLambda=*/true); @@ -10954,6 +10968,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; } @@ -13722,6 +13740,8 @@ void Sema::AddOverloadedCallCandidates(UnresolvedLookupExpr *ULE, CandidateSet, PartialOverloading, /*KnownValid*/ true); + CandidateSet.eliminateDuplicatesWithUnusedDefaultArgs(); + if (ULE->requiresADL()) AddArgumentDependentLookupCandidates(ULE->getName(), ULE->getExprLoc(), Args, ExplicitTemplateArgs, @@ -14175,13 +14195,54 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, break; } - case OR_Ambiguous: + case OR_Ambiguous: { + auto handleAmbiguousDefaultArgs = [&] { + unsigned FirstDefaultArgIndex = Args.size(); + llvm::SmallVector Notes; + for (const OverloadCandidate &Cand : *CandidateSet) { + if (!Cand.Best) + continue; + if (isa(Cand.Function) || + Cand.Function->isFunctionTemplateSpecialization()) { + return false; + } + if (!Cand.ViableByDefaultArgument) { + // We found a candidate marked best which wasn't made viable by + // default argument, which means this is not an "ambiguous by + // default argument" situation. + return false; + } + const ParmVarDecl *Parameter = + Cand.Function->getParamDecl(FirstDefaultArgIndex); + Notes.emplace_back( + Parameter->getDefaultArg()->getExprLoc(), + SemaRef.PDiag(diag::note_default_argument_declared_here) + << Parameter->getDefaultArgRange()); + } + if (Notes.empty()) + return false; + + assert(Notes.size() >= 2 && + "Overloaded call is considered ambiguous by default arguments," + " but we found less than two candidates"); + SemaRef.Diag(Fn->getBeginLoc(), diag::err_ovl_ambiguous_default_arg) + << Fn->getSourceRange(); + llvm::sort(Notes); + for (const PartialDiagnosticAt &PDiag : Notes) { + SemaRef.Diag(PDiag.first, PDiag.second); + } + return true; + }; + if (handleAmbiguousDefaultArgs()) + break; + CandidateSet->NoteCandidates( PartialDiagnosticAt(Fn->getBeginLoc(), SemaRef.PDiag(diag::err_ovl_ambiguous_call) << ULE->getName() << Fn->getSourceRange()), SemaRef, OCD_AmbiguousCandidates, Args); break; + } case OR_Deleted: { FunctionDecl *FDecl = (*Best)->Function; diff --git a/clang/test/CXX/drs/cwg0xx.cpp b/clang/test/CXX/drs/cwg0xx.cpp index 44a0eb520af22..d115a8f9da691 100644 --- a/clang/test/CXX/drs/cwg0xx.cpp +++ b/clang/test/CXX/drs/cwg0xx.cpp @@ -11,48 +11,54 @@ // 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: 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; 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 default argument that has multiple definitions}} + // expected-note@#cwg1-X {{default argument declared here}} + // expected-note@#cwg1-Y {{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 default argument that has multiple definitions}} + // expected-note@#cwg1-X {{default argument declared here}} + // expected-note@#cwg1-Y {{default argument declared here}} } } 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); + 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 default argument that has multiple definitions}} + // expected-note@#cwg1-i {{default argument declared here}} + // expected-note@#cwg1-i-redecl {{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 default argument that has multiple definitions}} + // expected-note@#cwg1-i {{default argument declared here}} + // expected-note@#cwg1-i-redecl2 {{default argument declared here}} } } // namespace cwg1 diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp index 15bcc20b7fa2a..f6aa0b3ffb35c 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/CXX/drs/cwg4xx.cpp b/clang/test/CXX/drs/cwg4xx.cpp index bcaf7db04ad3b..7df9bb9361798 100644 --- a/clang/test/CXX/drs/cwg4xx.cpp +++ b/clang/test/CXX/drs/cwg4xx.cpp @@ -369,10 +369,12 @@ namespace cwg417 { // cwg417: no } } // namespace cwg417 -namespace cwg418 { // cwg418: no +namespace cwg418 { // cwg418: 21 namespace example1 { +void f1(int, int); void f1(int, int = 0); void f1(int = 0, int); +void f1(int, int); void g() { f1(); } } // namespace example1 @@ -385,7 +387,9 @@ namespace B { using A::f2; } namespace A { +void f2(int); void f2(int = 3); +void f2(int); } void g2() { using B::f2; @@ -395,13 +399,17 @@ void g2() { } } // namespace example2 -// example from [over.match.best]/4 +// based on example from [over.match.best]/4 namespace example3 { namespace A { -extern "C" void f(int = 5); +extern "C" void f(int); +extern "C" void f(int = 5); // #cwg418-ex3-A +extern "C" void f(int); } namespace B { -extern "C" void f(int = 5); +extern "C" void f(int); +extern "C" void f(int = 5); // #cwg418-ex3-B +extern "C" void f(int); } using A::f; @@ -409,7 +417,14 @@ using B::f; void use() { f(3); - f(); // FIXME: this should fail + f(); + // expected-error@-1 {{function call relies on default argument that has multiple definitions}} + // expected-note@#cwg418-ex3-A {{default argument declared here}} + // expected-note@#cwg418-ex3-B {{default argument declared here}} + example3::f(); + // expected-error@-1 {{function call relies on default argument that has multiple definitions}} + // expected-note@#cwg418-ex3-A {{default argument declared here}} + // expected-note@#cwg418-ex3-B {{default argument declared here}} } } // namespace example3 } // namespace cwg418 diff --git a/clang/test/CodeGenCXX/default-arguments.cpp b/clang/test/CodeGenCXX/default-arguments.cpp index 2459ef1ad41fc..002d9fa703ccf 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/Parser/function-parameter-limit.cpp b/clang/test/Parser/function-parameter-limit.cpp index 29f5dde294715..b543f6a7ac848 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 new file mode 100644 index 0000000000000..b87d7cb7b8d90 --- /dev/null +++ b/clang/test/SemaCXX/default-argument-different-scopes.cpp @@ -0,0 +1,186 @@ +// 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 f3(char); +void f4(int = 0); // #f4-global +void f5(int = 0); // #f5-global + +void f() { + using A::f1; + using B::f1; + + f1(); + f1(0); + f1(0, 0); + + using A::f2; + using B::f2; + + 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); + + using A::f3; + using B::f3; + + f3(); + // expected-error@-1 {{function call relies on default argument that has multiple definitions}} + // expected-note@#A-f3 {{default argument declared here}} + // expected-note@#B-f3 {{default argument declared here}} + f3(0); + f3(0, 0); + f3('a'); + + using ::f4; + void f4(int = 1); // #f4-local + f4(); + // expected-error@-1 {{function call relies on default argument that has multiple definitions}} + // expected-note@#f4-global {{default argument declared here}} + // expected-note@#f4-local {{default argument declared here}} + + using ::f5; + extern void f5(int = 1); // #f5-local + extern void f5(int); + f5(); + // expected-error@-1 {{function call relies on default argument that has multiple definitions}} + // expected-note@#f5-global {{default argument declared here}} + // expected-note@#f5-local {{default argument declared here}} +} + +// Two declarations in different scopes have to be found and be viable +// candidates to run into ambiguous default arguments situation. +// In the tests below, calls with qualified names finds declarations only +// at namespace scope, whereas calls with unqualified names find only +// declarations at block scope. As a result, all calls are well-formed +// in 'i' group of tests. + +void i1(int = 2); +void i2(int = 2); +extern "C" void j1(int = 2); +extern "C" void j2(int = 2); + +void i() { + void i1(int = 3); + ::i1(); + i1(); + + extern void i2(int = 3); + ::i2(); + i2(); + + void j1(int = 3); + ::j1(); + j1(); + + extern void j2(int = 3); + ::j2(); + j2(); +} + +// In 'k' group of tests, no redefinition of default arguments occur, +// because sets of default arguments are associated with lexical scopes +// of function declarations. There are exceptions from this rule, +// described below. + +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}} + +// The only exception from the rule that default arguments are associated with +// their lexical scope is out-of-line definitions of member functions of +// non-templated classes. Such default arguments contribute to the set of +// default arguments associated with the class scope. + +struct L { + void l1(int); + void l2(int = 2); + void l3(int = 3); // #l3 + + template + void l4(int); // #l4 + template + void l5(int = 5); + template + 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 +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 +void L::l5(int) {} + +template +void L::l6(int = 6) {} +// expected-error@-1 {{redefinition of default argument}} +// expected-note@#l6 {{previous definition is here}} + +// Default arguments are not allowed in out-of-line declarations +// of member functions of class templates. They have to be specified within +// member-specification. + +template +struct M { + void m1(int); + void m2(int = 2); + void m3(int = 3); // #m3 +}; + +template +void M::m1(int = 1) {} +// expected-error@-1 {{default arguments cannot be added to an out-of-line definition of a member of a class template}} + +template +void M::m2(int) {} + +// FIXME: the real problem is that default argument is not allowed here, +// and not that it's redefined. +template +void M::m3(int = 3) {} +// expected-error@-1 {{redefinition of default argument}} +// expected-note@#m3 {{previous definition is here}} diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 69ddd5e58b921..d8fb845b5beba 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -51,7 +51,7 @@

C++ defect report implementation status

1 TC1 What if two using-declarations refer to the same function but the declarations introduce different default-arguments? - No + Clang 21 2 @@ -2555,7 +2555,7 @@

C++ defect report implementation status

418 CD6 Imperfect wording on error on multiple default arguments on a called function - No + Clang 21 419