Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1533,6 +1533,10 @@ private: bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser *Diagnoser); + bool CheckMultiVersionFunction(FunctionDecl *NewFD, bool &Redeclaration, + NamedDecl *&OldDecl, + bool &MergeTypeWithPrevious, + LookupResult &Previous); struct ModuleScope { clang::Module *Module = nullptr; Index: lib/Sema/CMakeLists.txt =================================================================== --- lib/Sema/CMakeLists.txt +++ lib/Sema/CMakeLists.txt @@ -31,6 +31,7 @@ SemaDecl.cpp SemaDeclAttr.cpp SemaDeclCXX.cpp + SemaDeclMultiVersion.cpp SemaDeclObjC.cpp SemaExceptionSpec.cpp SemaExpr.cpp Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9159,341 +9159,6 @@ D->getFriendObjectKind() != Decl::FOK_None); } -/// \brief Check the target attribute of the function for MultiVersion -/// validity. -/// -/// Returns true if there was an error, false otherwise. -static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) { - const auto *TA = FD->getAttr(); - assert(TA && "MultiVersion Candidate requires a target attribute"); - TargetAttr::ParsedTargetAttr ParseInfo = TA->parse(); - const TargetInfo &TargetInfo = S.Context.getTargetInfo(); - enum ErrType { Feature = 0, Architecture = 1 }; - - if (!ParseInfo.Architecture.empty() && - !TargetInfo.validateCpuIs(ParseInfo.Architecture)) { - S.Diag(FD->getLocation(), diag::err_bad_multiversion_option) - << Architecture << ParseInfo.Architecture; - return true; - } - - for (const auto &Feat : ParseInfo.Features) { - auto BareFeat = StringRef{Feat}.substr(1); - if (Feat[0] == '-') { - S.Diag(FD->getLocation(), diag::err_bad_multiversion_option) - << Feature << ("no-" + BareFeat).str(); - return true; - } - - if (!TargetInfo.validateCpuSupports(BareFeat) || - !TargetInfo.isValidFeatureName(BareFeat)) { - S.Diag(FD->getLocation(), diag::err_bad_multiversion_option) - << Feature << BareFeat; - return true; - } - } - return false; -} - -static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, - const FunctionDecl *NewFD, - bool CausesMV) { - enum DoesntSupport { - FuncTemplates = 0, - VirtFuncs = 1, - DeducedReturn = 2, - Constructors = 3, - Destructors = 4, - DeletedFuncs = 5, - DefaultedFuncs = 6 - }; - enum Different { - CallingConv = 0, - ReturnType = 1, - ConstexprSpec = 2, - InlineSpec = 3, - StorageClass = 4, - Linkage = 5 - }; - - // For now, disallow all other attributes. These should be opt-in, but - // an analysis of all of them is a future FIXME. - if (CausesMV && OldFD && - std::distance(OldFD->attr_begin(), OldFD->attr_end()) != 1) { - S.Diag(OldFD->getLocation(), diag::err_multiversion_no_other_attrs); - S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); - return true; - } - - if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) != 1) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs); - - if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << FuncTemplates; - - if (const auto *NewCXXFD = dyn_cast(NewFD)) { - if (NewCXXFD->isVirtual()) - return S.Diag(NewCXXFD->getLocation(), - diag::err_multiversion_doesnt_support) - << VirtFuncs; - - if (const auto *NewCXXCtor = dyn_cast(NewFD)) - return S.Diag(NewCXXCtor->getLocation(), - diag::err_multiversion_doesnt_support) - << Constructors; - - if (const auto *NewCXXDtor = dyn_cast(NewFD)) - return S.Diag(NewCXXDtor->getLocation(), - diag::err_multiversion_doesnt_support) - << Destructors; - } - - if (NewFD->isDeleted()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << DeletedFuncs; - - if (NewFD->isDefaulted()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << DefaultedFuncs; - - QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType()); - const auto *NewType = cast(NewQType); - QualType NewReturnType = NewType->getReturnType(); - - if (NewReturnType->isUndeducedType()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << DeducedReturn; - - // Only allow transition to MultiVersion if it hasn't been used. - if (OldFD && CausesMV && OldFD->isUsed(false)) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_after_used); - - // Ensure the return type is identical. - if (OldFD) { - QualType OldQType = S.getASTContext().getCanonicalType(OldFD->getType()); - const auto *OldType = cast(OldQType); - FunctionType::ExtInfo OldTypeInfo = OldType->getExtInfo(); - FunctionType::ExtInfo NewTypeInfo = NewType->getExtInfo(); - - if (OldTypeInfo.getCC() != NewTypeInfo.getCC()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << CallingConv; - - QualType OldReturnType = OldType->getReturnType(); - - if (OldReturnType != NewReturnType) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << ReturnType; - - if (OldFD->isConstexpr() != NewFD->isConstexpr()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << ConstexprSpec; - - if (OldFD->isInlineSpecified() != NewFD->isInlineSpecified()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << InlineSpec; - - if (OldFD->getStorageClass() != NewFD->getStorageClass()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << StorageClass; - - if (OldFD->isExternC() != NewFD->isExternC()) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) - << Linkage; - - if (S.CheckEquivalentExceptionSpec( - OldFD->getType()->getAs(), OldFD->getLocation(), - NewFD->getType()->getAs(), NewFD->getLocation())) - return true; - } - return false; -} - -/// \brief Check the validity of a mulitversion function declaration. -/// Also sets the multiversion'ness' of the function itself. -/// -/// This sets NewFD->isInvalidDecl() to true if there was an error. -/// -/// Returns true if there was an error, false otherwise. -static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD, - bool &Redeclaration, NamedDecl *&OldDecl, - bool &MergeTypeWithPrevious, - LookupResult &Previous) { - const auto *NewTA = NewFD->getAttr(); - if (NewFD->isMain()) { - if (NewTA && NewTA->isDefaultVersion()) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_not_allowed_on_main); - NewFD->setInvalidDecl(); - return true; - } - return false; - } - - // If there is no matching previous decl, only 'default' can - // cause MultiVersioning. - if (!OldDecl) { - if (NewTA && NewTA->isDefaultVersion()) { - if (!NewFD->getType()->getAs()) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_noproto); - NewFD->setInvalidDecl(); - return true; - } - if (CheckMultiVersionAdditionalRules(S, nullptr, NewFD, true)) { - NewFD->setInvalidDecl(); - return true; - } - if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported); - NewFD->setInvalidDecl(); - return true; - } - - NewFD->setIsMultiVersion(); - } - return false; - } - - if (OldDecl->getDeclContext()->getRedeclContext() != - NewFD->getDeclContext()->getRedeclContext()) - return false; - - FunctionDecl *OldFD = OldDecl->getAsFunction(); - // Unresolved 'using' statements (the other way OldDecl can be not a function) - // likely cannot cause a problem here. - if (!OldFD) - return false; - - if (!OldFD->isMultiVersion() && !NewTA) - return false; - - if (OldFD->isMultiVersion() && !NewTA) { - S.Diag(NewFD->getLocation(), diag::err_target_required_in_redecl); - NewFD->setInvalidDecl(); - return true; - } - - TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse(); - // Sort order doesn't matter, it just needs to be consistent. - std::sort(NewParsed.Features.begin(), NewParsed.Features.end()); - - const auto *OldTA = OldFD->getAttr(); - if (!OldFD->isMultiVersion()) { - // If the old decl is NOT MultiVersioned yet, and we don't cause that - // to change, this is a simple redeclaration. - if (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr()) - return false; - - // Otherwise, this decl causes MultiVersioning. - if (!S.getASTContext().getTargetInfo().supportsMultiVersioning()) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_not_supported); - S.Diag(OldFD->getLocation(), diag::note_previous_declaration); - NewFD->setInvalidDecl(); - return true; - } - - if (!OldFD->getType()->getAs()) { - S.Diag(OldFD->getLocation(), diag::err_multiversion_noproto); - S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); - NewFD->setInvalidDecl(); - return true; - } - - if (CheckMultiVersionValue(S, NewFD)) { - NewFD->setInvalidDecl(); - return true; - } - - if (CheckMultiVersionValue(S, OldFD)) { - S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); - NewFD->setInvalidDecl(); - return true; - } - - TargetAttr::ParsedTargetAttr OldParsed = - OldTA->parse(std::less()); - - if (OldParsed == NewParsed) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); - S.Diag(OldFD->getLocation(), diag::note_previous_declaration); - NewFD->setInvalidDecl(); - return true; - } - - for (const auto *FD : OldFD->redecls()) { - const auto *CurTA = FD->getAttr(); - if (!CurTA || CurTA->isInherited()) { - S.Diag(FD->getLocation(), diag::err_target_required_in_redecl); - S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); - NewFD->setInvalidDecl(); - return true; - } - } - - if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true)) { - NewFD->setInvalidDecl(); - return true; - } - - OldFD->setIsMultiVersion(); - NewFD->setIsMultiVersion(); - Redeclaration = false; - MergeTypeWithPrevious = false; - OldDecl = nullptr; - Previous.clear(); - return false; - } - - bool UseMemberUsingDeclRules = - S.CurContext->isRecord() && !NewFD->getFriendObjectKind(); - - // 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)) - continue; - - const auto *CurTA = CurFD->getAttr(); - if (CurTA->getFeaturesStr() == NewTA->getFeaturesStr()) { - NewFD->setIsMultiVersion(); - Redeclaration = true; - OldDecl = ND; - return false; - } - - TargetAttr::ParsedTargetAttr CurParsed = - CurTA->parse(std::less()); - - if (CurParsed == NewParsed) { - S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); - S.Diag(CurFD->getLocation(), diag::note_previous_declaration); - NewFD->setInvalidDecl(); - return true; - } - } - - // Else, this is simply a non-redecl case. - if (CheckMultiVersionValue(S, NewFD)) { - NewFD->setInvalidDecl(); - return true; - } - - if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, false)) { - NewFD->setInvalidDecl(); - return true; - } - - NewFD->setIsMultiVersion(); - Redeclaration = false; - MergeTypeWithPrevious = false; - OldDecl = nullptr; - Previous.clear(); - return false; -} - /// \brief Perform semantic checking of a new function declaration. /// /// Performs semantic analysis of the new function declaration @@ -9581,7 +9246,7 @@ } } - if (CheckMultiVersionFunction(*this, NewFD, Redeclaration, OldDecl, + if (CheckMultiVersionFunction(NewFD, Redeclaration, OldDecl, MergeTypeWithPrevious, Previous)) return Redeclaration; Index: lib/Sema/SemaDeclMultiVersion.cpp =================================================================== --- lib/Sema/SemaDeclMultiVersion.cpp +++ lib/Sema/SemaDeclMultiVersion.cpp @@ -0,0 +1,353 @@ +//==SemaDeclMultiVersion.cpp - Semantic Analysis for MultiVersion Functions===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for MultiVersion Function +// Declarations. +// +//===----------------------------------------------------------------------===// + +#include "clang/Sema/SemaInternal.h" + +using namespace clang; +using namespace sema; + +/// \brief Check the target attribute of the function for MultiVersion +/// validity. +/// +/// Returns true if there was an error, false otherwise. +static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) { + const auto *TA = FD->getAttr(); + assert(TA && "MultiVersion Candidate requires a target attribute"); + TargetAttr::ParsedTargetAttr ParseInfo = TA->parse(); + const TargetInfo &TargetInfo = S.Context.getTargetInfo(); + enum ErrType { Feature = 0, Architecture = 1 }; + + if (!ParseInfo.Architecture.empty() && + !TargetInfo.validateCpuIs(ParseInfo.Architecture)) { + S.Diag(FD->getLocation(), diag::err_bad_multiversion_option) + << Architecture << ParseInfo.Architecture; + return true; + } + + for (const auto &Feat : ParseInfo.Features) { + auto BareFeat = StringRef{Feat}.substr(1); + if (Feat[0] == '-') { + S.Diag(FD->getLocation(), diag::err_bad_multiversion_option) + << Feature << ("no-" + BareFeat).str(); + return true; + } + + if (!TargetInfo.validateCpuSupports(BareFeat) || + !TargetInfo.isValidFeatureName(BareFeat)) { + S.Diag(FD->getLocation(), diag::err_bad_multiversion_option) + << Feature << BareFeat; + return true; + } + } + return false; +} + +static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, + const FunctionDecl *NewFD, + bool CausesMV) { + enum DoesntSupport { + FuncTemplates = 0, + VirtFuncs = 1, + DeducedReturn = 2, + Constructors = 3, + Destructors = 4, + DeletedFuncs = 5, + DefaultedFuncs = 6 + }; + enum Different { + CallingConv = 0, + ReturnType = 1, + ConstexprSpec = 2, + InlineSpec = 3, + StorageClass = 4, + Linkage = 5 + }; + + // For now, disallow all other attributes. These should be opt-in, but + // an analysis of all of them is a future FIXME. + if (CausesMV && OldFD && + std::distance(OldFD->attr_begin(), OldFD->attr_end()) != 1) { + S.Diag(OldFD->getLocation(), diag::err_multiversion_no_other_attrs); + S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + } + + if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) != 1) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs); + + if (NewFD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << FuncTemplates; + + if (const auto *NewCXXFD = dyn_cast(NewFD)) { + if (NewCXXFD->isVirtual()) + return S.Diag(NewCXXFD->getLocation(), + diag::err_multiversion_doesnt_support) + << VirtFuncs; + + if (const auto *NewCXXCtor = dyn_cast(NewFD)) + return S.Diag(NewCXXCtor->getLocation(), + diag::err_multiversion_doesnt_support) + << Constructors; + + if (const auto *NewCXXDtor = dyn_cast(NewFD)) + return S.Diag(NewCXXDtor->getLocation(), + diag::err_multiversion_doesnt_support) + << Destructors; + } + + if (NewFD->isDeleted()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << DeletedFuncs; + + if (NewFD->isDefaulted()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << DefaultedFuncs; + + QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType()); + const auto *NewType = cast(NewQType); + QualType NewReturnType = NewType->getReturnType(); + + if (NewReturnType->isUndeducedType()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << DeducedReturn; + + // Only allow transition to MultiVersion if it hasn't been used. + if (OldFD && CausesMV && OldFD->isUsed(false)) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_after_used); + + // Ensure the return type is identical. + if (OldFD) { + QualType OldQType = S.getASTContext().getCanonicalType(OldFD->getType()); + const auto *OldType = cast(OldQType); + FunctionType::ExtInfo OldTypeInfo = OldType->getExtInfo(); + FunctionType::ExtInfo NewTypeInfo = NewType->getExtInfo(); + + if (OldTypeInfo.getCC() != NewTypeInfo.getCC()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << CallingConv; + + QualType OldReturnType = OldType->getReturnType(); + + if (OldReturnType != NewReturnType) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << ReturnType; + + if (OldFD->isConstexpr() != NewFD->isConstexpr()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << ConstexprSpec; + + if (OldFD->isInlineSpecified() != NewFD->isInlineSpecified()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << InlineSpec; + + if (OldFD->getStorageClass() != NewFD->getStorageClass()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << StorageClass; + + if (OldFD->isExternC() != NewFD->isExternC()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << Linkage; + + if (S.CheckEquivalentExceptionSpec( + OldFD->getType()->getAs(), OldFD->getLocation(), + NewFD->getType()->getAs(), NewFD->getLocation())) + return true; + } + return false; +} + +/// \brief Check the validity of a mulitversion function declaration. +/// Also sets the multiversion'ness' of the function itself. +/// +/// This sets NewFD->isInvalidDecl() to true if there was an error. +/// +/// Returns true if there was an error, false otherwise. +bool Sema::CheckMultiVersionFunction(FunctionDecl *NewFD, + bool &Redeclaration, NamedDecl *&OldDecl, + bool &MergeTypeWithPrevious, + LookupResult &Previous) { + const auto *NewTA = NewFD->getAttr(); + if (NewFD->isMain()) { + if (NewTA && NewTA->isDefaultVersion()) { + Diag(NewFD->getLocation(), diag::err_multiversion_not_allowed_on_main); + NewFD->setInvalidDecl(); + return true; + } + return false; + } + + // If there is no matching previous decl, only 'default' can + // cause MultiVersioning. + if (!OldDecl) { + if (NewTA && NewTA->isDefaultVersion()) { + if (!NewFD->getType()->getAs()) { + Diag(NewFD->getLocation(), diag::err_multiversion_noproto); + NewFD->setInvalidDecl(); + return true; + } + if (CheckMultiVersionAdditionalRules(*this, nullptr, NewFD, true)) { + NewFD->setInvalidDecl(); + return true; + } + if (!getASTContext().getTargetInfo().supportsMultiVersioning()) { + Diag(NewFD->getLocation(), diag::err_multiversion_not_supported); + NewFD->setInvalidDecl(); + return true; + } + + NewFD->setIsMultiVersion(); + } + return false; + } + + if (OldDecl->getDeclContext()->getRedeclContext() != + NewFD->getDeclContext()->getRedeclContext()) + return false; + + FunctionDecl *OldFD = OldDecl->getAsFunction(); + // Unresolved 'using' statements (the other way OldDecl can be not a function) + // likely cannot cause a problem here. + if (!OldFD) + return false; + + if (!OldFD->isMultiVersion() && !NewTA) + return false; + + if (OldFD->isMultiVersion() && !NewTA) { + Diag(NewFD->getLocation(), diag::err_target_required_in_redecl); + NewFD->setInvalidDecl(); + return true; + } + + TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse(); + // Sort order doesn't matter, it just needs to be consistent. + std::sort(NewParsed.Features.begin(), NewParsed.Features.end()); + + const auto *OldTA = OldFD->getAttr(); + if (!OldFD->isMultiVersion()) { + // If the old decl is NOT MultiVersioned yet, and we don't cause that + // to change, this is a simple redeclaration. + if (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr()) + return false; + + // Otherwise, this decl causes MultiVersioning. + if (!getASTContext().getTargetInfo().supportsMultiVersioning()) { + Diag(NewFD->getLocation(), diag::err_multiversion_not_supported); + Diag(OldFD->getLocation(), diag::note_previous_declaration); + NewFD->setInvalidDecl(); + return true; + } + + if (!OldFD->getType()->getAs()) { + Diag(OldFD->getLocation(), diag::err_multiversion_noproto); + Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + NewFD->setInvalidDecl(); + return true; + } + + if (CheckMultiVersionValue(*this, NewFD)) { + NewFD->setInvalidDecl(); + return true; + } + + if (CheckMultiVersionValue(*this, OldFD)) { + Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + NewFD->setInvalidDecl(); + return true; + } + + TargetAttr::ParsedTargetAttr OldParsed = + OldTA->parse(std::less()); + + if (OldParsed == NewParsed) { + Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); + Diag(OldFD->getLocation(), diag::note_previous_declaration); + NewFD->setInvalidDecl(); + return true; + } + + for (const auto *FD : OldFD->redecls()) { + const auto *CurTA = FD->getAttr(); + if (!CurTA || CurTA->isInherited()) { + Diag(FD->getLocation(), diag::err_target_required_in_redecl); + Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + NewFD->setInvalidDecl(); + return true; + } + } + + if (CheckMultiVersionAdditionalRules(*this, OldFD, NewFD, true)) { + NewFD->setInvalidDecl(); + return true; + } + + OldFD->setIsMultiVersion(); + NewFD->setIsMultiVersion(); + Redeclaration = false; + MergeTypeWithPrevious = false; + OldDecl = nullptr; + Previous.clear(); + return false; + } + + bool UseMemberUsingDeclRules = + CurContext->isRecord() && !NewFD->getFriendObjectKind(); + + // 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 (IsOverload(NewFD, CurFD, UseMemberUsingDeclRules)) + continue; + + const auto *CurTA = CurFD->getAttr(); + if (CurTA->getFeaturesStr() == NewTA->getFeaturesStr()) { + NewFD->setIsMultiVersion(); + Redeclaration = true; + OldDecl = ND; + return false; + } + + TargetAttr::ParsedTargetAttr CurParsed = + CurTA->parse(std::less()); + + if (CurParsed == NewParsed) { + Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); + Diag(CurFD->getLocation(), diag::note_previous_declaration); + NewFD->setInvalidDecl(); + return true; + } + } + + // Else, this is simply a non-redecl case. + if (CheckMultiVersionValue(*this, NewFD)) { + NewFD->setInvalidDecl(); + return true; + } + + if (CheckMultiVersionAdditionalRules(*this, OldFD, NewFD, false)) { + NewFD->setInvalidDecl(); + return true; + } + + NewFD->setIsMultiVersion(); + Redeclaration = false; + MergeTypeWithPrevious = false; + OldDecl = nullptr; + Previous.clear(); + return false; +}