[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1056341: g++-12: rejects valid C++ with templates/auto



Package: g++-12
Version: 12.2.0-14
Severity: normal
Tags: upstream
X-Debbugs-Cc: david.turner+deb@raspberrypi.com

Dear Maintainer,

The attached C++ file fails to compile in GCC 12.2 using the command
`g++ --std=c++17 -c 1862683_reproducer.cpp`, but works correctly with 12.3 and
with clang (godbolt link: https://godbolt.org/z/q4dGbTcvb).

I came across this while building Firefox for Raspberry Pi OS using the Debian
packaging scripts - this bug prevents Firefox from being built on Debian stable
using GCC (the reproducer is simplified down from Firefox 119).

Given this works in 12.3 it's almost certainly known and fixed upstream, but
I'm not sure which upstream problem report would apply (possibly 106893 or
105623?).  Please could the fix for this be backported to stable?

Kind regards,
David

-- System Information:
Debian Release: 12.2
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable-debug'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 6.1.0-13-amd64 (SMP w/20 CPU threads; PREEMPT)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), LANGUAGE=en_GB:en
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages g++-12 depends on:
ii  gcc-12            12.2.0-14
ii  gcc-12-base       12.2.0-14
ii  libc6             2.36-9+deb12u3
ii  libgmp10          2:6.2.1+dfsg1-1.1
ii  libisl23          0.25-1
ii  libmpc3           1.3.1-1
ii  libmpfr6          4.2.0-1
ii  libstdc++-12-dev  12.2.0-14
ii  libzstd1          1.5.4+dfsg2-5
ii  zlib1g            1:1.2.13.dfsg-1

g++-12 recommends no packages.

Versions of packages g++-12 suggests:
pn  g++-12-multilib  <none>
ii  gcc-12-doc       12.2.0-1

-- no debconf information
#include <utility> // std::forward
#include <stdint.h>  // {,u}int8_t, {,u}int16_t, {,u}int32_t

enum Type {
    Int8,
    Uint8,
};

namespace JS {

template <Type ArrayType> struct ExternalTypeOf {};
template <> struct ExternalTypeOf<Int8> { using Type = int8_t; };
template <> struct ExternalTypeOf<Uint8> { using Type = uint8_t; };

template <Type ArrayType>
using ExternalTypeOf_t = typename ExternalTypeOf<ArrayType>::Type;

struct ArrayBuffer { using DataType = uint8_t; };
struct ArrayBufferView { using DataType = uint8_t; };

template <Type TypedArrayElementType>
struct TypedArray {
  using DataType = ExternalTypeOf_t<TypedArrayElementType>;
};

using Int8Array = TypedArray<Int8>;
using Uint8Array = TypedArray<Uint8>;

}  // namespace JS

template <typename element_type>
struct Span {
  element_type *mData;
  int mLength;

  Span(element_type *aData, int aLength) : mData(aData), mLength(aLength) {}
};

template <class ArrayT>
struct TypedArray_base {
  using element_type = typename ArrayT::DataType;

 public:
  template <typename Processor> void ProcessData(
      Processor&& aProcessor) const {
    aProcessor(GetCurrentData());
  }

 private:
  Span<element_type> GetCurrentData() const {
    element_type* mData = nullptr;
    uint32_t mLength = 0;
    return Span(mData, mLength);
  }
};

template <class ArrayT>
struct TypedArray : public TypedArray_base<ArrayT> {
  using Base = TypedArray_base<ArrayT>;
  using element_type = typename Base::element_type;

  TypedArray() = default;
};

struct ArrayBufferView_base : public TypedArray_base<JS::ArrayBufferView> {
 private:
  using Base = TypedArray_base<JS::ArrayBufferView>;
};

using Int8Array = TypedArray<JS::Int8Array>;
using Uint8Array = TypedArray<JS::Uint8Array>;
using ArrayBufferView = ArrayBufferView_base;
using ArrayBuffer = TypedArray<JS::ArrayBuffer>;

template <typename Union, typename UnionMemberType, typename = int>
struct ApplyToTypedArray;

#define APPLY_IMPL(type)                                                        \
  template <typename Union>                                                     \
  struct ApplyToTypedArray<Union, type, decltype((void)&Union::Is##type, 0)> {  \
    /* Return type of calling the lambda with a TypedArray 'type'.         */   \
    template <typename F>                                                       \
    using FunReturnType = decltype(std::declval<F>()(std::declval<type>()));    \
                                                                                \
    template <typename F>                                                       \
    using ApplyReturnType =                                                     \
        std::conditional_t<true, FunReturnType<F>, FunReturnType<F>>;   \
                                                                                \
   public:                                                                      \
    template <typename F>                                                       \
    static ApplyReturnType<F> Apply(const Union& aUnion, F&& aFun) {            \
      if (!aUnion.Is##type()) {                                                 \
        return ApplyReturnType<F>(); /* false or Nothing() */                   \
      }                                                                         \
      if constexpr (true) {                                        \
        std::forward<F>(aFun)(aUnion.GetAs##type());                            \
        return;                                                            \
      } else {                                                                  \
        return Some(std::forward<F>(aFun)(aUnion.GetAs##type()));               \
      }                                                                         \
    }                                                                           \
  };

APPLY_IMPL(Int8Array) // XXX DWT comment this for workaround XXX
APPLY_IMPL(Uint8Array)
APPLY_IMPL(ArrayBufferView)
APPLY_IMPL(ArrayBuffer)

#undef APPLY_IMPL

// The binding code generate creates an alias of this type for every WebIDL
// union that contains a typed array type, with the right value for H (which
// will be true if there are non-typedarray types in the union).
template <typename T, bool H, typename FirstUnionMember,
          typename... UnionMembers>
struct ApplyToTypedArraysHelper {
  template <typename Fun>
  static void Apply(const T& aUnion, Fun&& aFun) {
    ApplyToTypedArray<T, FirstUnionMember>::Apply(
        aUnion, std::forward<Fun>(aFun));
  }
};

template <typename T, typename Fun>
void ApplyToTypedArrays(const T& aUnion, Fun&& aFun) {
  using ApplyToTypedArrays = typename T::ApplyToTypedArrays;

  ApplyToTypedArrays::template Apply<Fun>(aUnion, std::forward<Fun>(aFun));
}

template <typename T, typename Processor>
void ProcessTypedArrays(const T& aUnion, Processor&& aProcessor) {
  ApplyToTypedArrays(
      aUnion, [&](const auto& aTypedArray) {
        aTypedArray.ProcessData(std::forward<Processor>(aProcessor));
      });
}

class ArrayBufferViewOrArrayBuffer {
public:
  using ApplyToTypedArrays = ApplyToTypedArraysHelper<ArrayBufferViewOrArrayBuffer, false, ArrayBufferView, ArrayBuffer>;

private:
  ArrayBufferViewOrArrayBuffer(const ArrayBufferViewOrArrayBuffer&) = delete;
  ArrayBufferViewOrArrayBuffer& operator=(const ArrayBufferViewOrArrayBuffer&) = delete;
public:
  explicit inline ArrayBufferViewOrArrayBuffer() { }
  inline ~ArrayBufferViewOrArrayBuffer() { }
  inline bool IsArrayBufferView() const { return true; }
  inline bool IsArrayBuffer() const { return true; }

  inline ArrayBufferView const & GetAsArrayBufferView() const
  {
    return *((ArrayBufferView*)0);
  }

  inline ArrayBuffer const & GetAsArrayBuffer() const
  {
    return *((ArrayBuffer*)0);
  }
};

void Foo(const ArrayBufferViewOrArrayBuffer& aSource) {
  ProcessTypedArrays(
      aSource, [&](const Span<uint8_t>& aData) {
      });
}
1862683_reproducer.cpp: In instantiation of ‘void TypedArray_base<ArrayT>::ProcessData(Processor&&) const [with Processor = Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>; ArrayT = JS::TypedArray<Int8>]’:
1862683_reproducer.cpp:135:9:   required from ‘ProcessTypedArrays<ArrayBufferViewOrArrayBuffer, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)> >(const ArrayBufferViewOrArrayBuffer&, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>&&)::<lambda(const auto:1&)> [with auto:1 = TypedArray<JS::TypedArray<Int8> >]’
1862683_reproducer.cpp:104:1:   required by substitution of ‘template<class Union> template<class F> using ApplyReturnType = std::conditional_t<true, decltype (declval<F>()(std::declval<TypedArray<JS::TypedArray<Int8> > >())), decltype (declval<F>()(std::declval<TypedArray<JS::TypedArray<Int8> > >()))> [with F = ProcessTypedArrays<ArrayBufferViewOrArrayBuffer, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)> >(const ArrayBufferViewOrArrayBuffer&, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>&&)::<lambda(const auto:1&)>; Union = ArrayBufferViewOrArrayBuffer]’
1862683_reproducer.cpp:106:1:   required by substitution of ‘template<class F> static ApplyToTypedArray<ArrayBufferViewOrArrayBuffer, ArrayBufferView_base, int>::ApplyReturnType<F> ApplyToTypedArray<ArrayBufferViewOrArrayBuffer, ArrayBufferView_base, int>::Apply(const ArrayBufferViewOrArrayBuffer&, F&&) [with F = ProcessTypedArrays<ArrayBufferViewOrArrayBuffer, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)> >(const ArrayBufferViewOrArrayBuffer&, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>&&)::<lambda(const auto:1&)>]’
1862683_reproducer.cpp:119:50:   required from ‘static void ApplyToTypedArraysHelper<T, H, FirstUnionMember, UnionMembers>::Apply(const T&, Fun&&) [with Fun = ProcessTypedArrays<ArrayBufferViewOrArrayBuffer, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)> >(const ArrayBufferViewOrArrayBuffer&, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>&&)::<lambda(const auto:1&)>; T = ArrayBufferViewOrArrayBuffer; bool H = false; FirstUnionMember = ArrayBufferView_base; UnionMembers = {TypedArray<JS::ArrayBuffer>}]’
1862683_reproducer.cpp:128:42:   required from ‘void ApplyToTypedArrays(const T&, Fun&&) [with T = ArrayBufferViewOrArrayBuffer; Fun = ProcessTypedArrays<ArrayBufferViewOrArrayBuffer, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)> >(const ArrayBufferViewOrArrayBuffer&, Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>&&)::<lambda(const auto:1&)>]’
1862683_reproducer.cpp:133:21:   required from ‘void ProcessTypedArrays(const T&, Processor&&) [with T = ArrayBufferViewOrArrayBuffer; Processor = Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>]’
1862683_reproducer.cpp:164:21:   required from here
1862683_reproducer.cpp:46:15: error: no match for call to ‘(Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>) (Span<signed char>)’
   46 |     aProcessor(GetCurrentData());
      |     ~~~~~~~~~~^~~~~~~~~~~~~~~~~~
1862683_reproducer.cpp:165:16: note: candidate: ‘Foo(const ArrayBufferViewOrArrayBuffer&)::<lambda(const Span<unsigned char>&)>’
  165 |       aSource, [&](const Span<uint8_t>& aData) {
      |                ^
1862683_reproducer.cpp:165:16: note:   no known conversion for argument 1 from ‘Span<signed char>’ to ‘const Span<unsigned char>&’

Reply to: