Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 9 additions & 2 deletions clang/lib/Sema/SemaTemplateDeductionGuide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,11 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
// !!NOTE: DeduceResults respects the sequence of template parameters of
// the deduction guide f.
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
if (const auto &D = DeduceResults[Index]; !D.isNull()) // Deduced
const auto &D = DeduceResults[Index];
bool NonDeduced =
D.isNull() || (D.getKind() == TemplateArgument::Pack &&
D.pack_size() == 1 && D.pack_begin()->isNull());
if (!NonDeduced)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that be a function? Should isNull be changed? When do we get in that situation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added explanations.

DeducedArgs.push_back(D);
else
NonDeducedTemplateParamsInFIndex.push_back(Index);
Expand Down Expand Up @@ -1141,7 +1145,10 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
Args.addOuterTemplateArguments(TransformedDeducedAliasArgs);
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
const auto &D = DeduceResults[Index];
if (D.isNull()) {
bool NonDeduced =
D.isNull() || (D.getKind() == TemplateArgument::Pack &&
D.pack_size() == 1 && D.pack_begin()->isNull());
if (NonDeduced) {
// 2): Non-deduced template parameters would be substituted later.
continue;
}
Expand Down
31 changes: 20 additions & 11 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,16 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
return std::nullopt;
}

static TemplateArgument
getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getSubstitutedTemplateArgumentPattern would be a better name. Please add a comment too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pre-existing - do I have to rename that?

assert(S.ArgumentPackSubstitutionIndex >= 0);
assert(S.ArgumentPackSubstitutionIndex < (int)Arg.pack_size());
Arg = Arg.pack_begin()[S.ArgumentPackSubstitutionIndex];
if (Arg.isPackExpansion())
Arg = Arg.getPackExpansionPattern();
return Arg;
}

//===----------------------------------------------------------------------===/
// Template Instantiation for Types
//===----------------------------------------------------------------------===/
Expand Down Expand Up @@ -1467,10 +1477,16 @@ namespace {
}
}

static TemplateArgument
bool HeuristicallyComputeSizeOfPackExpr() const {
return !TemplateArgs.isRewrite();
}

TemplateArgument
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static is gone

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is intended because we need to access SemaRef.ArgumentPackSubstitutionIndex

getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
if (TA.getKind() != TemplateArgument::Pack)
return TA;
if (SemaRef.ArgumentPackSubstitutionIndex != -1)
return getPackSubstitutedTemplateArgument(SemaRef, TA);
assert(TA.pack_size() == 1 &&
"unexpected pack arguments in template rewrite");
TemplateArgument Arg = *TA.pack_begin();
Expand Down Expand Up @@ -1630,6 +1646,9 @@ namespace {
std::vector<TemplateArgument> TArgs;
switch (Arg.getKind()) {
case TemplateArgument::Pack:
assert(SemaRef.CodeSynthesisContexts.empty() ||
SemaRef.CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::BuildingDeductionGuides);
// Literally rewrite the template argument pack, instead of unpacking
// it.
for (auto &pack : Arg.getPackAsArray()) {
Expand Down Expand Up @@ -1869,16 +1888,6 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) {
return true;
}

static TemplateArgument
getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) {
assert(S.ArgumentPackSubstitutionIndex >= 0);
assert(S.ArgumentPackSubstitutionIndex < (int)Arg.pack_size());
Arg = Arg.pack_begin()[S.ArgumentPackSubstitutionIndex];
if (Arg.isPackExpansion())
Arg = Arg.getPackExpansionPattern();
return Arg;
}

Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
if (!D)
return nullptr;
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -3660,6 +3660,8 @@ class TreeTransform {
return SemaRef.BuildCXXNoexceptExpr(Range.getBegin(), Arg, Range.getEnd());
}

bool HeuristicallyComputeSizeOfPackExpr() const { return true; }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need a comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to add a TransformSizeOfPackExpr override in TemplateInstantiator that calls
back to the base implementation with a bool parameter, or something like that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I choose to extract a function ComputeSizeOfPackExprWithoutSubstitution, since there is some common logic for extracting the pack pattern beforehand. Thus overriding TransformSizeOfPackExpr might not be suitable due to duplication.

(We can't add boolean parameters to the function either, as it is expanded from macros.)

/// Build a new expression to compute the length of a parameter pack.
ExprResult RebuildSizeOfPackExpr(SourceLocation OperatorLoc, NamedDecl *Pack,
SourceLocation PackLoc,
Expand Down Expand Up @@ -16095,6 +16097,10 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
// Try to compute the result without performing a partial substitution.
std::optional<unsigned> Result = 0;
for (const TemplateArgument &Arg : PackArgs) {
if (!getDerived().HeuristicallyComputeSizeOfPackExpr()) {
Result = std::nullopt;
break;
}
if (!Arg.isPackExpansion()) {
Result = *Result + 1;
continue;
Expand Down
115 changes: 114 additions & 1 deletion clang/test/SemaCXX/ctad.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++20 %s
// expected-no-diagnostics

namespace GH64347 {

Expand All @@ -17,3 +16,117 @@ void k() {
}

} // namespace GH64347

namespace GH123591 {


template < typename... _Types >
struct variant {
template <int N = sizeof...(_Types)>
variant(_Types...);
};

template <class T>
using AstNode = variant<T, T, T>;

AstNode tree(42, 43, 44);

}

namespace GH123591_2 {

template <int>
using enable_if_t = char;

template < typename... Types >
struct variant {
template < enable_if_t<sizeof...(Types)>>
variant();
};

template <int>
using AstNode = variant<>;
// expected-note@-1 {{couldn't infer template argument ''}} \
// expected-note@-1 2{{implicit deduction guide declared as}} \
// expected-note@-1 {{candidate function template not viable}}


AstNode tree; // expected-error {{no viable constructor or deduction guide}}

}

namespace GH127539 {

template <class...>
struct A {
template <class... ArgTs>
A(ArgTs...) {}
};

template <class... ArgTs>
A(ArgTs...) -> A<typename ArgTs::value_type...>;

template <class... Ts>
using AA = A<Ts...>;

AA a{};

}

namespace GH129077 {

using size_t = decltype(sizeof(0));

struct index_type
{
size_t value{~0ull};
index_type() = default;
constexpr index_type(size_t i) noexcept : value(i) {}
};

template <index_type... Extents>
struct extents
{
constexpr extents(decltype(Extents)...) noexcept {}
};

template <class... Extents>
extents(Extents...) -> extents<(requires { Extents::value; } ? Extents{} : ~0ull)...>;

template <index_type... Index>
using index = extents<Index...>;

int main()
{
extents i{0,0};
auto j = extents<64,{}>({}, 42);

index k{0,0};
auto l = index<64,{}>({}, 42);

return 0;
}

}

namespace GH129998 {

struct converible_to_one {
constexpr operator int() const noexcept { return 1; }
};

template <int... Extents>
struct class_template {
class_template() = default;
constexpr class_template(auto&&...) noexcept {}
};

template <class... Extents>
class_template(Extents...) -> class_template<(true ? 0 : +Extents{})...>;

template <int... Extents>
using alias_template = class_template<Extents...>;

alias_template var2{converible_to_one{}, 2};

}