[clang] Add missing diagnostics for invalid overloads of multiversion functions in C.

Previously, an attempt to declare an overload of a multiversion function
in C was not properly diagnosed. In some cases, diagnostics were simply
missing. In other cases the following assertion failure occured...
```
Assertion `(Previous.empty() || llvm::any_of(Previous, [](const NamedDecl *ND) { return ND->hasAttr(); })) && "Non-redecls shouldn't happen without overloadable present"' failed.
```
... or the following diagnostic was spuriously issued.
```
error: at most one overload for a given name may lack the 'overloadable' attribute
```

The diagnostics issued in some cases could be improved. When the function
type of a redeclaration does not match the prior declaration, it would be
preferable to diagnose the type mismatch before diagnosing mismatched
attributes. Diagnostics are also missing for some cases.

Reviewed By: erichkeane

Differential Revision: https://reviews.llvm.org/D121959
This commit is contained in:
Tom Honermann 2022-02-09 15:02:13 -08:00 committed by Tom Honermann
parent 42e4c5b261
commit 8b6f1cbb21
4 changed files with 37 additions and 32 deletions

View File

@ -1460,27 +1460,38 @@ void Sema::ActOnExitFunctionContext() {
assert(CurContext && "Popped translation unit!");
}
/// Determine whether we allow overloading of the function
/// PrevDecl with another declaration.
/// Determine whether overloading is allowed for a new function
/// declaration considering prior declarations of the same name.
///
/// This routine determines whether overloading is possible, not
/// whether some new function is actually an overload. It will return
/// true in C++ (where we can always provide overloads) or, as an
/// extension, in C when the previous function is already an
/// overloaded function declaration or has the "overloadable"
/// attribute.
static bool AllowOverloadingOfFunction(LookupResult &Previous,
/// whether a new declaration actually overloads a previous one.
/// It will return true in C++ (where overloads are alway permitted)
/// or, as a C extension, when either the new declaration or a
/// previous one is declared with the 'overloadable' attribute.
static bool AllowOverloadingOfFunction(const LookupResult &Previous,
ASTContext &Context,
const FunctionDecl *New) {
if (Context.getLangOpts().CPlusPlus)
if (Context.getLangOpts().CPlusPlus || New->hasAttr<OverloadableAttr>())
return true;
if (Previous.getResultKind() == LookupResult::FoundOverloaded)
return true;
// Multiversion function declarations are not overloads in the
// usual sense of that term, but lookup will report that an
// overload set was found if more than one multiversion function
// declaration is present for the same name. It is therefore
// inadequate to assume that some prior declaration(s) had
// the overloadable attribute; checking is required. Since one
// declaration is permitted to omit the attribute, it is necessary
// to check at least two; hence the 'any_of' check below. Note that
// the overloadable attribute is implicitly added to declarations
// that were required to have it but did not.
if (Previous.getResultKind() == LookupResult::FoundOverloaded) {
return llvm::any_of(Previous, [](const NamedDecl *ND) {
return ND->hasAttr<OverloadableAttr>();
});
} else if (Previous.getResultKind() == LookupResult::Found)
return Previous.getFoundDecl()->hasAttr<OverloadableAttr>();
return Previous.getResultKind() == LookupResult::Found &&
(Previous.getFoundDecl()->hasAttr<OverloadableAttr>() ||
New->hasAttr<OverloadableAttr>());
return false;
}
/// Add this decl to the scope shadowed decl chains.
@ -10705,13 +10716,17 @@ static bool CheckMultiVersionAdditionalDecl(
bool UseMemberUsingDeclRules =
S.CurContext->isRecord() && !NewFD->getFriendObjectKind();
bool MayNeedOverloadableChecks =
AllowOverloadingOfFunction(Previous, S.Context, NewFD);
// Next, check ALL non-overloads to see if this is a redeclaration of a
// previous member of the MultiVersion set.
for (NamedDecl *ND : Previous) {
FunctionDecl *CurFD = ND->getAsFunction();
if (!CurFD)
continue;
if (S.IsOverload(NewFD, CurFD, UseMemberUsingDeclRules))
if (MayNeedOverloadableChecks &&
S.IsOverload(NewFD, CurFD, UseMemberUsingDeclRules))
continue;
switch (NewMVType) {

View File

@ -1,5 +1,4 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -Wno-strict-prototypes -fsyntax-only -verify %s -Wnonnull
// XFAIL: asserts
void __attribute__((cpu_specific(ivybridge))) no_default(void);
void __attribute__((cpu_specific(sandybridge))) no_default(void);
@ -120,13 +119,9 @@ int __attribute__((cpu_dispatch(pentium_iii, pentium_iii_no_xmm_regs))) dupe_p3(
void __attribute__((cpu_specific(atom), nothrow, nonnull(1))) addtl_attrs(int*);
// FIXME: Declaration of a non-overloadable function when more than one
// FIXME: multiversion function declarations are present results in an
// FIXME: assertion failure.
int __attribute__((cpu_specific(atom))) bad_overload1(void);
int __attribute__((cpu_specific(ivybridge))) bad_overload1(void);
// expected-error@+2 {{at most one overload for a given name may lack the 'overloadable' attribute}}
// expected-note@-2 {{previous unmarked overload of function is here}}
// expected-error@+1 {{function declaration is missing 'cpu_specific' or 'cpu_dispatch' attribute in a multiversioned function}}
int bad_overload1(int);
int bad_overload2(int);
@ -135,13 +130,9 @@ int bad_overload2(int);
int __attribute__((cpu_specific(atom))) bad_overload2(void);
int __attribute__((cpu_specific(ivybridge))) bad_overload2(void);
// FIXME: Declaration of a non-overloadable function when more than one
// FIXME: multiversion function declarations are present results in an
// FIXME: assertion failure.
int __attribute__((cpu_dispatch(generic))) bad_overload3(void);
int __attribute__((cpu_specific(ivybridge))) bad_overload3(void);
// expected-error@+2 {{at most one overload for a given name may lack the 'overloadable' attribute}}
// expected-note@-2 {{previous unmarked overload of function is here}}
// expected-error@+1 {{function declaration is missing 'cpu_specific' or 'cpu_dispatch' attribute in a multiversioned function}}
int bad_overload3(int);
int bad_overload4(int);

View File

@ -90,9 +90,13 @@ void bad_overload1(void) __attribute__((target_clones("mmx", "sse4.2", "default"
void bad_overload1(int p) {}
void bad_overload2(int p) {}
// expected-error@+2 {{conflicting types for 'bad_overload2'}}
// expected-note@-2 {{previous definition is here}}
void bad_overload2(void) __attribute__((target_clones("mmx", "sse4.2", "default")));
void bad_overload3(void) __attribute__((target_clones("mmx", "sse4.2", "default")));
// expected-error@+2 {{conflicting types for 'bad_overload3'}}
// expected-note@-2 {{previous declaration is here}}
void bad_overload3(int) __attribute__((target_clones("mmx", "sse4.2", "default")));
void good_overload1(void) __attribute__((target_clones("mmx", "sse4.2", "default")));

View File

@ -1,5 +1,4 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -Wno-strict-prototypes -fsyntax-only -verify %s
// XFAIL: asserts
void __attribute__((target("sse4.2"))) no_default(void);
void __attribute__((target("arch=sandybridge"))) no_default(void);
@ -109,13 +108,9 @@ void __attribute__((target("arch=sandybridge"))) addtl_attrs5(int*);
void __attribute__((target("sse4.2"))) addtl_attrs6(int*);
void __attribute__((target("arch=sandybridge"), nothrow, used, nonnull)) addtl_attrs6(int*);
// FIXME: Declaration of a non-overloadable function when more than one
// FIXME: multiversion function declarations are present results in an
// FIXME: assertion failure.
int __attribute__((target("sse4.2"))) bad_overload1(void);
int __attribute__((target("arch=sandybridge"))) bad_overload1(void);
// expected-error@+2 {{at most one overload for a given name may lack the 'overloadable' attribute}}
// expected-note@-2 {{previous unmarked overload of function is here}}
// expected-error@+1 {{function declaration is missing 'target' attribute in a multiversioned function}}
int bad_overload1(int);
int bad_overload2(int);