-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang] Diagnose default arguments defined in different scopes #124844
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
base: main
Are you sure you want to change the base?
Changes from 17 commits
da30f70
a703624
c911cba
1a9664e
5a76dc9
ce204e4
f47e8cc
bfa5851
084fc66
e7411f8
8c27c02
b250d90
afdf43b
344cd4c
0e24e37
2bc0b2a
59c9a93
51c9e41
91546ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5145,6 +5145,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">; | ||||||||||||||
|
|
||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| } // let Deferrable | ||||||||||||||
|
|
||||||||||||||
| // C++11 Literal Operators | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,20 @@ 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. | ||
| // | ||
| // 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; | ||
|
|
||
|
||
| // We found the right previous declaration. | ||
| break; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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) { | ||||||
|
||||||
| for (int i = FirstDefaultArgIndex; i <= LastDefaultArgIndex; ++i) { | |
| for (int I = FirstDefaultArgIndex; I <= LastDefaultArgIndex; ++I) { |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you iterate over i in the outer loop, you can forgo the map entirely and do it on one pass
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10960,6 +10960,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. | ||
|
|
||
|
Comment on lines
+10971
to
+10974
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we have test for something like that
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ideally we would return
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have I think it's worth pointing out that when there is a redeclaration chain, only one declaration out of it is considered a candidate function for overload resolution. At least as far as I saw in the debugger while working on this. |
||
| return OR_Success; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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(); | ||
|
Comment on lines
+30
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that really related to the change?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was intended as a test for the code that emits diagnostics, but I need to update it, yeah. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an aside should we augment our #GHXXXX logic to support #CWG123 and #LWG123? :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would make some sense, but I still like how clickable those links are. #GHXXXX logic optimizes for writing release notes instead of reviewing them, which I find suboptimal.