reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
// RUN: %clang_cc1 -fsyntax-only %s -verify
// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify
// RUN: %clang_cc1 -std=c++17 -fsyntax-only %s -verify

struct AnyT {
  template<typename T>
  operator T();
};

void test_cvqual_ref(AnyT any) {
  const int &cir = any;  
}

struct AnyThreeLevelPtr {
  template<typename T>
  operator T***() const {
    T x = 0; // expected-note 2{{declared const here}}
    x = 0; // expected-error 2{{const-qualified type}}
    T ***p;
    return p;
  }
};

struct X { };

void test_deduce_with_qual(AnyThreeLevelPtr a3) {
  int * const * const * const ip1 = a3;
  // FIXME: This is wrong; we are supposed to deduce 'T = int' here.
  const int * const * const * const ip2 = a3; // expected-note {{instantiation of}}
  // This one is correct, though.
  const double * * * ip3 = a3; // expected-note {{instantiation of}}
}

struct AnyPtrMem {
  template<typename Class, typename T>
  operator T Class::*() const
  {
    // This is correct: we don't need a qualification conversion here, so we
    // deduce 'T = const float'.
    T x = 0; // expected-note {{declared const here}}
    x = 0; // expected-error {{const-qualified type}}
    return 0;
  }
};

void test_deduce_ptrmem_with_qual(AnyPtrMem apm) {
  const float X::* pm = apm; // expected-note {{instantiation of}}
}

struct TwoLevelPtrMem {
  template<typename Class1, typename Class2, typename T>
  operator T Class1::*Class2::*() const
  {
    T x = 0; // expected-note 2{{declared const here}}
    x = 0; // expected-error 2{{const-qualified type}}
    return 0;
  }
};

void test_deduce_two_level_ptrmem_with_qual(TwoLevelPtrMem apm) {
  // FIXME: This is wrong: we should deduce T = 'float'
  const float X::* const X::* pm2 = apm; // expected-note {{instantiation of}}
  // This is correct: we don't need a qualification conversion, so we directly
  // deduce T = 'const double'
  const double X::* X::* pm1 = apm; // expected-note {{instantiation of}}
}

namespace non_ptr_ref_cv_qual {
  template<typename Expected>
  struct ConvToT {
    template<typename T> operator T() {
      using Check = T;
      using Check = Expected;
    }
  };
  const int test_conv_to_t_1 = ConvToT<int>();
  // We intentionally deviate from [temp.deduct.conv]p4 here, and also remove
  // the top-level cv-quaifiers from A *after* removing the reference type, if
  // P is not also a reference type. This matches what other compilers are
  // doing, and is necessary to support real-world code.
  const int &test_conv_to_t_2 = ConvToT<int>();

  // Example code that would be broken by the standard's rule.
  struct Dest {};
  Dest d1a((ConvToT<Dest>()));
  Dest d1b = ConvToT<Dest>();
  Dest &d2 = (d1a = ConvToT<Dest>());

  template<typename Expected>
  struct ConvToTRef {
    template<typename T> operator T&() {
      using Check = T;
      using Check = Expected;
    }
  };
  const int test_conv_to_t_ref_1 = ConvToTRef<int>();
  const int &test_conv_to_t_ref_2 = ConvToTRef<const int>();

  Dest d3a((ConvToTRef<const Dest>())); // initialize the copy ctor parameter with 'const Dest&'
  Dest d3b = ConvToTRef<Dest>(); // convert to non-const T via [over.match.copy]/1.2
  Dest &d4 = (d3a = ConvToTRef<const Dest>());

  template<typename Expected>
  struct ConvToConstT {
    template<typename T> operator const T() {
      using Check = T;
      using Check = Expected;
    }
  };
  const int test_conv_to_const_t_1 = ConvToConstT<int>();
  const int &test_conv_to_const_t_2 = ConvToConstT<int>();

  template<typename Expected>
  struct ConvToConstTRef {
    template<typename T> operator const T&() {
      using Check = T;
      using Check = Expected;
    }
  };
  const int test_conv_to_const_t_ref_1 = ConvToConstTRef<int>();
  const int &test_conv_to_const_t_ref_2 = ConvToConstTRef<int>();

  template <typename T, int N> using Arr = T[N];
  struct ConvToArr {
    template <int N>
    operator Arr<int, N> &() {
      static_assert(N == 3, "");
    }
  };
  int (&test_conv_to_arr_1)[3] = ConvToArr(); // ok
  const int (&test_conv_to_arr_2)[3] = ConvToArr(); // ok, with qualification conversion

#if __cplusplus >= 201702L
  template<bool Noexcept, typename T, typename ...U> using Function = T(U...) noexcept(Noexcept);
  template<bool Noexcept> struct ConvToFunction {
    template <typename T, typename ...U> operator Function<Noexcept, T, U...>&(); // expected-note {{candidate}}
  };
  void (&fn1)(int) noexcept(false) = ConvToFunction<false>();
  void (&fn2)(int) noexcept(true)  = ConvToFunction<false>(); // expected-error {{no viable}}
  void (&fn3)(int) noexcept(false) = ConvToFunction<true>();
  void (&fn4)(int) noexcept(true)  = ConvToFunction<true>();

  struct ConvToFunctionDeducingNoexcept {
    template <bool Noexcept, typename T, typename ...U> operator Function<Noexcept, T, U...>&();
  };
  void (&fn5)(int) noexcept(false) = ConvToFunctionDeducingNoexcept();
  void (&fn6)(int) noexcept(true)  = ConvToFunctionDeducingNoexcept();
#endif
}