Skip to content

Reland: [clang] unified CWG2398 and P0522 changes; finishes implementation of P3310 #124137

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

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ C++20 Feature Support

- Implemented module level lookup for C++20 modules. (#GH90154)

C++17 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- The implementation of the relaxed template template argument matching rules is
more complete and reliable, and should provide more accurate diagnostics.

Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -351,7 +355,8 @@ Resolutions to C++ Defect Reports
(`CWG2351: void{} <https://cplusplus.github.io/CWG/issues/2351.html>`_).

- Clang now has improved resolution to CWG2398, allowing class templates to have
default arguments deduced when partial ordering.
default arguments deduced when partial ordering, and better backwards compatibility
in overload resolution.

- Clang now allows comparing unequal object pointers that have been cast to ``void *``
in constant expressions. These comparisons always worked in non-constant expressions.
Expand Down Expand Up @@ -636,6 +641,10 @@ Improvements to Clang's diagnostics

- Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391.

- Clang now properly explains the reason a template template argument failed to
match a template template parameter, in terms of the C++17 relaxed matching rules
instead of the old ones.

- Don't emit duplicated dangling diagnostics. (#GH93386).

- Improved diagnostic when trying to befriend a concept. (#GH45182).
Expand Down Expand Up @@ -885,6 +894,8 @@ Bug Fixes to C++ Support
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
- When performing partial ordering of function templates, clang now checks that
the deduction was consistent. Fixes (#GH18291).
- Fixes to several issues in partial ordering of template template parameters, which
were documented in the test suite.
- Fixed an assertion failure about a constraint of a friend function template references to a value with greater
template depth than the friend function template. (#GH98258)
- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -5323,6 +5323,13 @@ def note_template_arg_refers_here_func : Note<
def err_template_arg_template_params_mismatch : Error<
"template template argument has different template parameters than its "
"corresponding template template parameter">;
def note_template_arg_template_params_mismatch : Note<
"template template argument has different template parameters than its "
"corresponding template template parameter">;
def err_non_deduced_mismatch : Error<
"could not match %diff{$ against $|types}0,1">;
def err_inconsistent_deduction : Error<
"conflicting deduction %diff{$ against $|types}0,1 for parameter">;
def err_template_arg_not_integral_or_enumeral : Error<
"non-type template argument of type %0 must have an integral or enumeration"
" type">;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Sema/Overload.h
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,11 @@ class Sema;
LLVM_PREFERRED_TYPE(bool)
unsigned TookAddressOfOverload : 1;

/// Have we matched any packs on the parameter side, versus any non-packs on
/// the argument side, in a context where the opposite matching is also
/// allowed?
bool HasMatchedPackOnParmToNonPackOnArg : 1;

/// True if the candidate was found using ADL.
LLVM_PREFERRED_TYPE(CallExpr::ADLCallKind)
unsigned IsADLCandidate : 1;
Expand Down Expand Up @@ -1006,6 +1011,7 @@ class Sema;
OverloadCandidate()
: IsSurrogate(false), IgnoreObjectArgument(false),
TookAddressOfOverload(false),
HasMatchedPackOnParmToNonPackOnArg(false),
IsADLCandidate(llvm::to_underlying(CallExpr::NotADL)),
RewriteKind(CRK_None) {}
};
Expand Down
43 changes: 31 additions & 12 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10169,7 +10169,8 @@ class Sema final : public SemaBase {
ADLCallKind IsADLCandidate = ADLCallKind::NotADL,
ConversionSequenceList EarlyConversions = {},
OverloadCandidateParamOrder PO = {},
bool AggregateCandidateDeduction = false);
bool AggregateCandidateDeduction = false,
bool HasMatchedPackOnParmToNonPackOnArg = false);

/// Add all of the function declarations in the given function set to
/// the overload candidate set.
Expand Down Expand Up @@ -10204,7 +10205,8 @@ class Sema final : public SemaBase {
bool SuppressUserConversions = false,
bool PartialOverloading = false,
ConversionSequenceList EarlyConversions = {},
OverloadCandidateParamOrder PO = {});
OverloadCandidateParamOrder PO = {},
bool HasMatchedPackOnParmToNonPackOnArg = false);

/// Add a C++ member function template as a candidate to the candidate
/// set, using template argument deduction to produce an appropriate member
Expand Down Expand Up @@ -10250,7 +10252,8 @@ class Sema final : public SemaBase {
CXXConversionDecl *Conversion, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
bool AllowExplicit, bool AllowResultConversion = true);
bool AllowExplicit, bool AllowResultConversion = true,
bool HasMatchedPackOnParmToNonPackOnArg = false);

/// Adds a conversion function template specialization
/// candidate to the overload set, using template argument deduction
Expand Down Expand Up @@ -11678,7 +11681,8 @@ class Sema final : public SemaBase {
SourceLocation RAngleLoc, unsigned ArgumentPackIndex,
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
CheckTemplateArgumentKind CTAK);
CheckTemplateArgumentKind CTAK, bool PartialOrdering,
bool *MatchedPackOnParmToNonPackOnArg);

/// Check that the given template arguments can be provided to
/// the given template, converting the arguments along the way.
Expand Down Expand Up @@ -11725,7 +11729,8 @@ class Sema final : public SemaBase {
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
bool UpdateArgsWithConversions = true,
bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false);
bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false,
bool *MatchedPackOnParmToNonPackOnArg = nullptr);

bool CheckTemplateTypeArgument(
TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg,
Expand Down Expand Up @@ -11759,7 +11764,9 @@ class Sema final : public SemaBase {
/// It returns true if an error occurred, and false otherwise.
bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
TemplateParameterList *Params,
TemplateArgumentLoc &Arg, bool IsDeduced);
TemplateArgumentLoc &Arg,
bool PartialOrdering,
bool *MatchedPackOnParmToNonPackOnArg);

void NoteTemplateLocation(const NamedDecl &Decl,
std::optional<SourceRange> ParamRange = {});
Expand Down Expand Up @@ -12270,8 +12277,8 @@ class Sema final : public SemaBase {
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
unsigned NumExplicitlySpecified, FunctionDecl *&Specialization,
sema::TemplateDeductionInfo &Info,
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs = nullptr,
bool PartialOverloading = false,
SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs,
bool PartialOverloading, bool PartialOrdering,
llvm::function_ref<bool()> CheckNonDependent = [] { return false; });

/// Perform template argument deduction from a function call
Expand Down Expand Up @@ -12305,7 +12312,8 @@ class Sema final : public SemaBase {
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info,
bool PartialOverloading, bool AggregateDeductionCandidate,
QualType ObjectType, Expr::Classification ObjectClassification,
bool PartialOrdering, QualType ObjectType,
Expr::Classification ObjectClassification,
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent);

/// Deduce template arguments when taking the address of a function
Expand Down Expand Up @@ -12458,8 +12466,9 @@ class Sema final : public SemaBase {
sema::TemplateDeductionInfo &Info);

bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
TemplateParameterList *PParam, TemplateDecl *AArg,
const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced);
TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg,
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
bool PartialOrdering, bool *MatchedPackOnParmToNonPackOnArg);

/// Mark which template parameters are used in a given expression.
///
Expand Down Expand Up @@ -12768,6 +12777,9 @@ class Sema final : public SemaBase {

/// We are instantiating a type alias template declaration.
TypeAliasTemplateInstantiation,

/// We are performing partial ordering for template template parameters.
PartialOrderingTTP,
} Kind;

/// Was the enclosing context a non-instantiation SFINAE context?
Expand Down Expand Up @@ -12989,6 +13001,12 @@ class Sema final : public SemaBase {
TemplateDecl *Entity, BuildingDeductionGuidesTag,
SourceRange InstantiationRange = SourceRange());

struct PartialOrderingTTP {};
/// \brief Note that we are partial ordering template template parameters.
InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc,
PartialOrderingTTP, TemplateDecl *PArg,
SourceRange InstantiationRange = SourceRange());

/// Note that we have finished instantiating this template.
void Clear();

Expand Down Expand Up @@ -13450,7 +13468,8 @@ class Sema final : public SemaBase {
bool InstantiateClassTemplateSpecialization(
SourceLocation PointOfInstantiation,
ClassTemplateSpecializationDecl *ClassTemplateSpec,
TemplateSpecializationKind TSK, bool Complain = true);
TemplateSpecializationKind TSK, bool Complain = true,
bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false);

/// Instantiates the definitions of all of the member
/// of the given class, which is an instantiation of a class template
Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/Sema/TemplateDeduction.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class TemplateDeductionInfo {
/// Have we suppressed an error during deduction?
bool HasSFINAEDiagnostic = false;

/// Have we matched any packs on the parameter side, versus any non-packs on
/// the argument side, in a context where the opposite matching is also
/// allowed?
bool MatchedPackOnParmToNonPackOnArg = false;

/// The template parameter depth for which we're performing deduction.
unsigned DeducedDepth;

Expand Down Expand Up @@ -87,6 +92,14 @@ class TemplateDeductionInfo {
return DeducedDepth;
}

bool hasMatchedPackOnParmToNonPackOnArg() const {
return MatchedPackOnParmToNonPackOnArg;
}

void setMatchedPackOnParmToNonPackOnArg() {
MatchedPackOnParmToNonPackOnArg = true;
}

/// Get the number of explicitly-specified arguments.
unsigned getNumExplicitArgs() const {
return ExplicitArgs;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "BuildingDeductionGuides";
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
return "TypeAliasTemplateInstantiation";
case CodeSynthesisContext::PartialOrderingTTP:
return "PartialOrderingTTP";
}
return "";
}
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3675,7 +3675,9 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit);
if (CheckTemplateArgument(
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
0, SugaredChecked, CanonicalChecked, CTAK_Specified) ||
0, SugaredChecked, CanonicalChecked, CTAK_Specified,
/*PartialOrdering=*/false,
/*MatchedPackOnParmToNonPackOnArg=*/nullptr) ||
Trap.hasErrorOccurred())
IsTemplate = false;
}
Expand Down
50 changes: 32 additions & 18 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6917,7 +6917,8 @@ void Sema::AddOverloadCandidate(
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
bool PartialOverloading, bool AllowExplicit, bool AllowExplicitConversions,
ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions,
OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) {
OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction,
bool HasMatchedPackOnParmToNonPackOnArg) {
const FunctionProtoType *Proto
= dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());
assert(Proto && "Functions without a prototype cannot be overloaded");
Expand All @@ -6936,7 +6937,8 @@ void Sema::AddOverloadCandidate(
AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(),
Expr::Classification::makeSimpleLValue(), Args,
CandidateSet, SuppressUserConversions,
PartialOverloading, EarlyConversions, PO);
PartialOverloading, EarlyConversions, PO,
HasMatchedPackOnParmToNonPackOnArg);
return;
}
// We treat a constructor like a non-member function, since its object
Expand Down Expand Up @@ -6979,6 +6981,8 @@ void Sema::AddOverloadCandidate(
CandidateSet.getRewriteInfo().getRewriteKind(Function, PO);
Candidate.IsADLCandidate = llvm::to_underlying(IsADLCandidate);
Candidate.ExplicitCallArguments = Args.size();
Candidate.HasMatchedPackOnParmToNonPackOnArg =
HasMatchedPackOnParmToNonPackOnArg;

// Explicit functions are not actually candidates at all if we're not
// allowing them in this context, but keep them around so we can point
Expand Down Expand Up @@ -7521,16 +7525,13 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType,
}
}

void
Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, QualType ObjectType,
Expr::Classification ObjectClassification,
ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
bool SuppressUserConversions,
bool PartialOverloading,
ConversionSequenceList EarlyConversions,
OverloadCandidateParamOrder PO) {
void Sema::AddMethodCandidate(
CXXMethodDecl *Method, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, QualType ObjectType,
Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
bool PartialOverloading, ConversionSequenceList EarlyConversions,
OverloadCandidateParamOrder PO, bool HasMatchedPackOnParmToNonPackOnArg) {
const FunctionProtoType *Proto
= dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());
assert(Proto && "Methods without a prototype cannot be overloaded");
Expand Down Expand Up @@ -7561,6 +7562,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
Candidate.TookAddressOfOverload =
CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet;
Candidate.ExplicitCallArguments = Args.size();
Candidate.HasMatchedPackOnParmToNonPackOnArg =
HasMatchedPackOnParmToNonPackOnArg;

bool IgnoreExplicitObject =
(Method->isExplicitObjectMemberFunction() &&
Expand Down Expand Up @@ -7731,8 +7734,8 @@ void Sema::AddMethodTemplateCandidate(
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = DeduceTemplateArguments(
MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info,
PartialOverloading, /*AggregateDeductionCandidate=*/false, ObjectType,
ObjectClassification,
PartialOverloading, /*AggregateDeductionCandidate=*/false,
/*PartialOrdering=*/false, ObjectType, ObjectClassification,
[&](ArrayRef<QualType> ParamTypes) {
return CheckNonDependentConversions(
MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
Expand Down Expand Up @@ -7770,7 +7773,8 @@ void Sema::AddMethodTemplateCandidate(
AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
ActingContext, ObjectType, ObjectClassification, Args,
CandidateSet, SuppressUserConversions, PartialOverloading,
Conversions, PO);
Conversions, PO,
Info.hasMatchedPackOnParmToNonPackOnArg());
}

/// Determine whether a given function template has a simple explicit specifier
Expand Down Expand Up @@ -7816,6 +7820,7 @@ void Sema::AddTemplateOverloadCandidate(
if (TemplateDeductionResult Result = DeduceTemplateArguments(
FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info,
PartialOverloading, AggregateCandidateDeduction,
/*PartialOrdering=*/false,
/*ObjectType=*/QualType(),
/*ObjectClassification=*/Expr::Classification(),
[&](ArrayRef<QualType> ParamTypes) {
Expand Down Expand Up @@ -7856,7 +7861,8 @@ void Sema::AddTemplateOverloadCandidate(
Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions,
PartialOverloading, AllowExplicit,
/*AllowExplicitConversions=*/false, IsADLCandidate, Conversions, PO,
Info.AggregateDeductionCandidateHasMismatchedArity);
Info.AggregateDeductionCandidateHasMismatchedArity,
Info.hasMatchedPackOnParmToNonPackOnArg());
}

bool Sema::CheckNonDependentConversions(
Expand Down Expand Up @@ -7978,7 +7984,8 @@ void Sema::AddConversionCandidate(
CXXConversionDecl *Conversion, DeclAccessPair FoundDecl,
CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit,
bool AllowExplicit, bool AllowResultConversion) {
bool AllowExplicit, bool AllowResultConversion,
bool HasMatchedPackOnParmToNonPackOnArg) {
assert(!Conversion->getDescribedFunctionTemplate() &&
"Conversion function templates use AddTemplateConversionCandidate");
QualType ConvType = Conversion->getConversionType().getNonReferenceType();
Expand Down Expand Up @@ -8023,6 +8030,8 @@ void Sema::AddConversionCandidate(
Candidate.FinalConversion.setAllToTypes(ToType);
Candidate.Viable = true;
Candidate.ExplicitCallArguments = 1;
Candidate.HasMatchedPackOnParmToNonPackOnArg =
HasMatchedPackOnParmToNonPackOnArg;

// Explicit functions are not actually candidates at all if we're not
// allowing them in this context, but keep them around so we can point
Expand Down Expand Up @@ -8224,7 +8233,8 @@ void Sema::AddTemplateConversionCandidate(
assert(Specialization && "Missing function template specialization?");
AddConversionCandidate(Specialization, FoundDecl, ActingDC, From, ToType,
CandidateSet, AllowObjCConversionOnExplicit,
AllowExplicit, AllowResultConversion);
AllowExplicit, AllowResultConversion,
Info.hasMatchedPackOnParmToNonPackOnArg());
}

void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
Expand Down Expand Up @@ -10576,6 +10586,10 @@ bool clang::isBetterOverloadCandidate(
isa<CXXConstructorDecl>(Cand2.Function))
return isa<CXXConstructorDecl>(Cand1.Function);

if (Cand1.HasMatchedPackOnParmToNonPackOnArg !=
Cand2.HasMatchedPackOnParmToNonPackOnArg)
return Cand2.HasMatchedPackOnParmToNonPackOnArg;

// -- F1 is a non-template function and F2 is a function template
// specialization, or, if not that,
bool Cand1IsSpecialization = Cand1.Function &&
Expand Down
Loading
Loading