Skip to content

Commit

Permalink
[clang] Implement CWG2398 provisional TTP matching to class templates
Browse files Browse the repository at this point in the history
This extends default argument deduction to cover class templates as
well, and also applies outside of partial ordering, adding to the
provisional wording introduced in #89807.

This solves some ambuguity introduced in P0522 regarding how
template template parameters are partially ordered, and should reduce
the negative impact of enabling `-frelaxed-template-template-args`
by default.

Given the following example:
```C++
template <class T1, class T2 = float> struct A;
template <class T3> struct B;

template <template <class T4> class TT1, class T5> struct B<TT1<T5>>;   // #1
template <class T6, class T7>                      struct B<A<T6, T7>>; // #2

template struct B<A<int>>;
```
Prior to P0522, `#2` was picked. Afterwards, this became ambiguous.
This patch restores the pre-P0522 behavior, `#2` is picked again.

As the consequences are not restricted to partial ordering,
the following code becomes valid:
```C++
template<class T, class U> struct A {};
A<int, float> v;
template<template<class> class TT> void f(TT<int>);

// OK: TT picks 'float' as the default argument for the second parameter.
void g() { f(v); }
```

Also, since 'f' deduced from `A<int, float>` is different from 'f'
deduced from `A<int, double>`, this implements an additional mangling
rule.

---

Since this changes provisional implementation of CWG2398 which has
not been released yet, and already contains a changelog entry,
we don't provide a changelog entry here.
  • Loading branch information
mizvekov committed Jun 19, 2024
1 parent 728fb23 commit b238961
Show file tree
Hide file tree
Showing 28 changed files with 629 additions and 257 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/DumpAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
TEMPLATE_KIND(SubstTemplateTemplateParm);
TEMPLATE_KIND(SubstTemplateTemplateParmPack);
TEMPLATE_KIND(UsingTemplate);
TEMPLATE_KIND(DeducedTemplate);
#undef TEMPLATE_KIND
}
llvm_unreachable("Unhandled NameKind enum");
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,7 @@ class CollectExtraHighlightings
case TemplateName::SubstTemplateTemplateParm:
case TemplateName::SubstTemplateTemplateParmPack:
case TemplateName::UsingTemplate:
case TemplateName::DeducedTemplate:
// Names that could be resolved to a TemplateDecl are handled elsewhere.
break;
}
Expand Down
11 changes: 9 additions & 2 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::ContextualFoldingSet<SubstTemplateTemplateParmPackStorage,
ASTContext&>
SubstTemplateTemplateParmPacks;
mutable llvm::ContextualFoldingSet<DeducedTemplateStorage, ASTContext &>
DeducedTemplates;

mutable llvm::ContextualFoldingSet<ArrayParameterType, ASTContext &>
ArrayParameterTypes;
Expand Down Expand Up @@ -2256,6 +2258,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
unsigned Index,
bool Final) const;

TemplateName getDeducedTemplateName(TemplateName Underlying,
DefaultArguments DefaultArgs) const;

enum GetBuiltinTypeError {
/// No error
GE_None,
Expand Down Expand Up @@ -2735,11 +2740,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// template name uses the shortest form of the dependent
/// nested-name-specifier, which itself contains all canonical
/// types, values, and templates.
TemplateName getCanonicalTemplateName(const TemplateName &Name) const;
TemplateName getCanonicalTemplateName(TemplateName Name,
bool IgnoreDeduced = false) const;

/// Determine whether the given template names refer to the same
/// template.
bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const;
bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y,
bool IgnoreDeduced = false) const;

/// Determine whether the two declarations refer to the same entity.
bool isSameEntity(const NamedDecl *X, const NamedDecl *Y) const;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/ASTImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,11 @@ class TypeSourceInfo;
/// the declarations it contains.
[[nodiscard]] llvm::Error ImportDefinition(Decl *From);

llvm::Error
ImportTemplateArguments(ArrayRef<TemplateArgument> FromArgs,
SmallVectorImpl<TemplateArgument> &ToArgs);
Expected<TemplateArgument> Import(const TemplateArgument &From);

/// Cope with a name conflict when importing a declaration into the
/// given context.
///
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/DependenceFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,11 @@ toTemplateNameDependence(NestedNameSpecifierDependence D) {
return Dependence(D).templateName();
}

inline TemplateNameDependence
toTemplateNameDependence(TemplateArgumentDependence D) {
return Dependence(D).templateName();
}

LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();

} // namespace clang
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,23 @@ let Class = PropertyTypeCase<TemplateName, "SubstTemplateTemplateParmPack"> in {
return ctx.getSubstTemplateTemplateParmPack(argumentPack, associatedDecl, index, final);
}]>;
}
let Class = PropertyTypeCase<TemplateName, "DeducedTemplate"> in {
def : ReadHelper<[{
auto DTS = node.getAsDeducedTemplateName();
}]>;
def : Property<"underlying", TemplateName> {
let Read = [{ DTS->getUnderlying() }];
}
def : Property<"startPos", UInt32> {
let Read = [{ DTS->getDefaultArguments().StartPos }];
}
def : Property<"defaultArgs", Array<TemplateArgument>> {
let Read = [{ DTS->getDefaultArguments().Args }];
}
def : Creator<[{
return ctx.getDeducedTemplateName(underlying, {startPos, defaultArgs});
}]>;
}

// Type cases for TemplateArgument.
def : PropertyTypeKind<TemplateArgument, TemplateArgumentKind,
Expand Down
63 changes: 60 additions & 3 deletions clang/include/clang/AST/TemplateName.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class NestedNameSpecifier;
enum OverloadedOperatorKind : int;
class OverloadedTemplateStorage;
class AssumedTemplateStorage;
class DeducedTemplateStorage;
struct PrintingPolicy;
class QualifiedTemplateName;
class SubstTemplateTemplateParmPackStorage;
Expand All @@ -50,16 +51,17 @@ class UncommonTemplateNameStorage {
enum Kind {
Overloaded,
Assumed, // defined in DeclarationName.h
Deduced,
SubstTemplateTemplateParm,
SubstTemplateTemplateParmPack
};

struct BitsTag {
LLVM_PREFERRED_TYPE(Kind)
unsigned Kind : 2;
unsigned Kind : 3;

// The template parameter index.
unsigned Index : 15;
unsigned Index : 14;

/// The pack index, or the number of stored templates
/// or template arguments, depending on which subclass we have.
Expand Down Expand Up @@ -90,6 +92,12 @@ class UncommonTemplateNameStorage {
: nullptr;
}

DeducedTemplateStorage *getAsDeducedTemplateName() {
return Bits.Kind == Deduced
? reinterpret_cast<DeducedTemplateStorage *>(this)
: nullptr;
}

SubstTemplateTemplateParmStorage *getAsSubstTemplateTemplateParm() {
return Bits.Kind == SubstTemplateTemplateParm
? reinterpret_cast<SubstTemplateTemplateParmStorage *>(this)
Expand Down Expand Up @@ -172,6 +180,15 @@ class SubstTemplateTemplateParmPackStorage : public UncommonTemplateNameStorage,
unsigned Index, bool Final);
};

struct DefaultArguments {
// The position in the template parameter list
// the first argument corresponds to.
unsigned StartPos;
ArrayRef<TemplateArgument> Args;

operator bool() const { return !Args.empty(); }
};

/// Represents a C++ template name within the type system.
///
/// A C++ template name refers to a template within the C++ type
Expand Down Expand Up @@ -245,6 +262,10 @@ class TemplateName {
/// A template name that refers to a template declaration found through a
/// specific using shadow declaration.
UsingTemplate,

/// A template name that refers to another TemplateName with deduced default
/// arguments.
DeducedTemplate,
};

TemplateName() = default;
Expand All @@ -256,6 +277,7 @@ class TemplateName {
explicit TemplateName(QualifiedTemplateName *Qual);
explicit TemplateName(DependentTemplateName *Dep);
explicit TemplateName(UsingShadowDecl *Using);
explicit TemplateName(DeducedTemplateStorage *Deduced);

/// Determine whether this template name is NULL.
bool isNull() const;
Expand All @@ -270,7 +292,13 @@ class TemplateName {
/// to, if any. If the template name does not refer to a specific
/// declaration because it is a dependent name, or if it refers to a
/// set of function templates, returns NULL.
TemplateDecl *getAsTemplateDecl() const;
TemplateDecl *getAsTemplateDecl(bool IgnoreDeduced = false) const;

/// Retrieves the underlying template declaration that
/// this template name refers to, along with the
/// deduced default arguments, if any.
std::pair<TemplateDecl *, DefaultArguments>
getTemplateDeclAndDefaultArgs() const;

/// Retrieve the underlying, overloaded function template
/// declarations that this template name refers to, if known.
Expand Down Expand Up @@ -312,6 +340,11 @@ class TemplateName {
/// template declaration is introduced, if any.
UsingShadowDecl *getAsUsingShadowDecl() const;

/// Retrieve the deduced template info, if any.
DeducedTemplateStorage *getAsDeducedTemplateName() const;

std::optional<TemplateName> desugar(bool IgnoreDeduced) const;

TemplateName getUnderlying() const;

TemplateNameDependence getDependence() const;
Expand Down Expand Up @@ -411,6 +444,30 @@ class SubstTemplateTemplateParmStorage
std::optional<unsigned> PackIndex);
};

class DeducedTemplateStorage : public UncommonTemplateNameStorage,
public llvm::FoldingSetNode {
friend class ASTContext;

TemplateName Underlying;

DeducedTemplateStorage(TemplateName Underlying,
const DefaultArguments &DefArgs);

public:
TemplateName getUnderlying() const { return Underlying; }

DefaultArguments getDefaultArguments() const {
return {/*StartPos=*/Bits.Index,
/*Args=*/{reinterpret_cast<const TemplateArgument *>(this + 1),
Bits.Data}};
}

void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) const;

static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
TemplateName Underlying, const DefaultArguments &DefArgs);
};

inline TemplateName TemplateName::getUnderlying() const {
if (SubstTemplateTemplateParmStorage *subst
= getAsSubstTemplateTemplateParm())
Expand Down
10 changes: 7 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -9216,6 +9216,9 @@ class Sema final : public SemaBase {
/// receive true if the cause for the error is the associated constraints of
/// the template not being satisfied by the template arguments.
///
/// \param DefaultArgs any default arguments from template specialization
/// deduction.
///
/// \param PartialOrderingTTP If true, assume these template arguments are
/// the injected template arguments for a template template parameter.
/// This will relax the requirement that all its possible uses are valid:
Expand All @@ -9225,7 +9228,8 @@ class Sema final : public SemaBase {
/// \returns true if an error occurred, false otherwise.
bool CheckTemplateArgumentList(
TemplateDecl *Template, SourceLocation TemplateLoc,
TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs,
TemplateArgumentListInfo &TemplateArgs,
const DefaultArguments &DefaultArgs, bool PartialTemplateArgs,
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
bool UpdateArgsWithConversions = true,
Expand Down Expand Up @@ -9724,8 +9728,8 @@ class Sema final : public SemaBase {
sema::TemplateDeductionInfo &Info);

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

void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
unsigned Depth, llvm::SmallBitVector &Used);
Expand Down
Loading

0 comments on commit b238961

Please sign in to comment.