Skip to content

Commit d3bf0bb

Browse files
committed
PR24076, PR33655, C++ CWG 1558: Consider the instantiation-dependence of
the nested-name-specifier when determining whether a qualified type is instantiation-dependent.
1 parent d4ec334 commit d3bf0bb

File tree

7 files changed

+132
-4
lines changed

7 files changed

+132
-4
lines changed

clang/include/clang/AST/Type.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5398,7 +5398,9 @@ class ElaboratedType final
53985398
ElaboratedType(ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS,
53995399
QualType NamedType, QualType CanonType, TagDecl *OwnedTagDecl)
54005400
: TypeWithKeyword(Keyword, Elaborated, CanonType,
5401-
NamedType->getDependence()),
5401+
NamedType->getDependence() |
5402+
(NNS ? toTypeDependence(NNS->getDependence())
5403+
: TypeDependence::None)),
54025404
NNS(NNS), NamedType(NamedType) {
54035405
ElaboratedTypeBits.HasOwnedTagDecl = false;
54045406
if (OwnedTagDecl) {

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2578,6 +2578,10 @@ void CXXNameMangler::mangleType(QualType T) {
25782578
if (!TST->isTypeAlias())
25792579
break;
25802580

2581+
// FIXME: We presumably shouldn't strip off ElaboratedTypes with
2582+
// instantation-dependent qualifiers. See
2583+
// https://github.com/itanium-cxx-abi/cxx-abi/issues/114.
2584+
25812585
QualType Desugared
25822586
= T.getSingleStepDesugaredType(Context.getASTContext());
25832587
if (Desugared == T)

clang/test/CXX/drs/dr15xx.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,20 @@ namespace dr1550 { // dr1550: yes
239239
}
240240
}
241241

242+
namespace dr1558 { // dr1558: 12
243+
#if __cplusplus >= 201103L
244+
template<class T, class...> using first_of = T;
245+
template<class T> first_of<void, typename T::type> f(int); // expected-note {{'int' cannot be used prior to '::'}}
246+
template<class T> void f(...) = delete; // expected-note {{deleted}}
247+
248+
struct X { typedef void type; };
249+
void test() {
250+
f<X>(0);
251+
f<int>(0); // expected-error {{deleted}}
252+
}
253+
#endif
254+
}
255+
242256
namespace dr1560 { // dr1560: 3.5
243257
void f(bool b, int n) {
244258
(b ? throw 0 : n) = (b ? n : throw 0) = 0;

clang/test/CodeGenCXX/mangle-template.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,23 @@ namespace fixed_size_parameter_pack {
342342
template<int ...Ns> void f(A<unsigned, char, long long>::B<0, Ns...>);
343343
void g() { f<1, 2>({}); }
344344
}
345+
346+
namespace type_qualifier {
347+
template<typename T> using int_t = int;
348+
template<typename T> void f(decltype(int_t<T*>() + 1)) {}
349+
// FIXME: This mangling doesn't work: we need to mangle the
350+
// instantiation-dependent 'int_t' operand.
351+
// CHECK: @_ZN14type_qualifier1fIPiEEvDTplcvi_ELi1EE
352+
template void f<int*>(int);
353+
354+
// Note that this template has different constraints but would mangle the
355+
// same:
356+
//template<typename T> void f(decltype(int_t<typename T::type>() + 1)) {}
357+
358+
struct impl { using type = void; };
359+
template<typename T> using alias = impl;
360+
template<typename T> void g(decltype(alias<T*>::type(), 1)) {}
361+
// FIXME: Similarly we need to mangle the `T*` in here.
362+
// CHECK: @_ZN14type_qualifier1gIPiEEvDTcmcvv_ELi1EE
363+
template void g<int*>(int);
364+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %clang_cc1 -std=c++2b -verify %s
2+
3+
// Ensure we substitute into instantiation-dependent but non-dependent
4+
// constructs. The poster-child for this is...
5+
template<class ...> using void_t = void;
6+
7+
namespace PR24076 {
8+
template<class T> T declval();
9+
struct s {};
10+
11+
template<class T,
12+
class = void_t<decltype(declval<T>() + 1)>>
13+
void foo(T) {} // expected-note {{invalid operands to binary expression}}
14+
15+
void f() {
16+
foo(s{}); // expected-error {{no matching function}}
17+
}
18+
19+
template<class T,
20+
class = void_t<decltype(declval<T>() + 1)>> // expected-error {{invalid operands to binary expression}}
21+
struct bar {};
22+
23+
bar<s> bar; // expected-note {{in instantiation of}}
24+
}
25+
26+
namespace PR33655 {
27+
struct One { using x = int; };
28+
struct Two { using y = int; };
29+
30+
template<typename T, void_t<typename T::x> * = nullptr> int &func() {}
31+
template<typename T, void_t<typename T::y> * = nullptr> float &func() {}
32+
33+
int &test1 = func<One>();
34+
float &test2 = func<Two>();
35+
36+
template<class ...Args> struct indirect_void_t_imp { using type = void; };
37+
template<class ...Args> using indirect_void_t = typename indirect_void_t_imp<Args...>::type;
38+
39+
template<class T> void foo() {
40+
static_assert(!__is_void(indirect_void_t<T>)); // "ok", dependent
41+
static_assert(!__is_void(void_t<T>)); // expected-error {{failed}}
42+
}
43+
}
44+
45+
namespace PR46791 { // also PR45782
46+
template<typename T, typename = void>
47+
struct trait {
48+
static constexpr int specialization = 0;
49+
};
50+
51+
// FIXME: Per a strict interpretation of the C++ rules, the two void_t<...>
52+
// types below are equivalent -- we only (effectively) do token-by-token
53+
// comparison for *expressions* appearing within types. But all other
54+
// implementations accept this, using rules that are unclear.
55+
template<typename T>
56+
struct trait<T, void_t<typename T::value_type>> { // expected-note {{previous}} FIXME-note {{matches}}
57+
static constexpr int specialization = 1;
58+
};
59+
60+
template<typename T>
61+
struct trait<T, void_t<typename T::element_type>> { // expected-error {{redefinition}} FIXME-note {{matches}}
62+
static constexpr int specialization = 2;
63+
};
64+
65+
struct A {};
66+
struct B { typedef int value_type; };
67+
struct C { typedef int element_type; };
68+
struct D : B, C {};
69+
70+
static_assert(trait<A>::specialization == 0);
71+
static_assert(trait<B>::specialization == 1); // FIXME expected-error {{failed}}
72+
static_assert(trait<C>::specialization == 2); // FIXME expected-error {{failed}}
73+
static_assert(trait<D>::specialization == 0); // FIXME-error {{ambiguous partial specialization}}
74+
}

clang/test/SemaTemplate/partial-spec-instantiate.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ namespace rdar9169404 {
5151
X<bool, -1>::type value;
5252
#if __cplusplus >= 201103L
5353
// expected-error@-2 {{non-type template argument evaluates to -1, which cannot be narrowed to type 'bool'}}
54-
#else
55-
// expected-no-diagnostics
5654
#endif
5755
}
5856

@@ -98,3 +96,19 @@ namespace rdar39524996 {
9896
takesWrapperInContainer(c);
9997
}
10098
}
99+
100+
namespace InstantiationDependent {
101+
template<typename> using ignore = void; // expected-warning 0-1{{extension}}
102+
template<typename T, typename = void> struct A {
103+
static const bool specialized = false;
104+
};
105+
template<typename T> struct Hide { typedef void type; };
106+
template<typename T> struct A<T, Hide<ignore<typename T::type> >::type> {
107+
static const bool specialized = true;
108+
};
109+
110+
struct X {};
111+
struct Y { typedef int type; };
112+
_Static_assert(!A<X>::specialized, "");
113+
_Static_assert(A<Y>::specialized, "");
114+
}

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9163,7 +9163,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
91639163
<td><a href="https://wg21.link/cwg1558">1558</a></td>
91649164
<td>CD4</td>
91659165
<td>Unused arguments in alias template specializations</td>
9166-
<td class="none" align="center">Unknown</td>
9166+
<td class="unreleased" align="center">Clang 12</td>
91679167
</tr>
91689168
<tr id="1559">
91699169
<td><a href="https://wg21.link/cwg1559">1559</a></td>

0 commit comments

Comments
 (0)