Skip to content

False positive compile error when expanding packs in function parameters #58452

@yabinc

Description

@yabinc

Repro: https://godbolt.org/z/G6e3cK4W9

template <typename A, typename B>
using ConditionalRewrite = B;

template <typename T>
using SignatureType = int;

template <typename... Args>
struct Type1 {
  template <typename... Params>
  using Return = SignatureType<int(ConditionalRewrite<Args, Params>...)>;

};

template <typename... Args>
struct Type2 {
  using T1 = Type1<Args...>;

  template <typename... Params>
  using Return = typename T1::template Return<Params...>;

};

template <typename T>
typename T::template Return<int, int> InvokeMethod() {
  return 3;
}

int Function1() {
  return InvokeMethod<Type2<int, int>>();
}

When compiling the above code, Clang reports below error:

<source>:10:3: error: pack expansion contains parameter packs 'Args' and 'Params' that have different lengths (2 vs. 1)
  using Return = SignatureType<int(ConditionalRewrite<Args, Params>...)>;
  ^

However, the length of Args and Params are the same when instantiating Type2<int, int>::Return<int, int>.
The problem happens after https://reviews.llvm.org/D131802.

My understanding of the problem is below:

The error was reported in https://github.com/root-project/root/blob/master/interpreter/llvm/src/tools/clang/lib/Sema/SemaTemplateVariadic.cpp#L727 in function Sema::CheckParameterPacksForExpansion(). CheckParameterPacksForExpansion() checks if the lengths of all unexpanded
parameter packs are the same.

The expansion we care in the example is in L10 ConditionalRewrite<Args, Params>.... Since we instantiate Type1<int, int>,
Args is a pack TemplateArgument: <int, int>, which has pack length 2. But for Params, its content changes in multiple runs of
CheckParameterPacksForExpansion. It can be either <type-parameter-0-0...> or <int, int>. And when Params is <type-parameter-0-0...>, CheckParameterPacksForExpansion() thinks its length is 1 (which doesn't match the length of Args), and reports the error.

I experimented adding below lines in CheckParameterPacksForExpansion() to skip the length check when a template arg is expandable:

      const TemplateArgument& Arg = TemplateArgs(Depth, Index);
      if (Arg.pack_size() == 1 && Arg.pack_begin()->isPackExpansion()) {
        ShouldExpand = false;
        continue;
      }

It can avoid the compile error. But I don't know if there are other cases to consider. So I'd like to leave it to maintainers for a proper fix.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

Status
Done

Relationships

None yet

Development

No branches or pull requests

Issue actions