diff options
| author | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-09-12 23:28:08 +0000 |
|---|---|---|
| committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-09-12 23:28:08 +0000 |
| commit | 1fff95c7023d343bee9f0e633169c91452ec5368 (patch) | |
| tree | a116b518447dd2185b0bac01c78fb71fcce82f1b /clang/test | |
| parent | 6b3e6d54871f02cdaca323a80051f2d744b39b76 (diff) | |
| download | bcm5719-llvm-1fff95c7023d343bee9f0e633169c91452ec5368.tar.gz bcm5719-llvm-1fff95c7023d343bee9f0e633169c91452ec5368.zip | |
PR13657 (and duplicates):
When a comma occurs in a default argument or default initializer within a
class, disambiguate whether it is part of the initializer or whether it ends
the initializer.
The way this works (which I will be proposing for standardization) is to treat
the comma as ending the default argument or default initializer if the
following token sequence matches the syntactic constraints of a
parameter-declaration-clause or init-declarator-list (respectively).
This is both consistent with the disambiguation rules elsewhere (where entities
are treated as declarations if they can be), and should have no regressions
over our old behavior. I think it might also disambiguate all cases correctly,
but I don't have a proof of that.
There is an annoyance here: because we're performing a tentative parse in a
situation where we may not have seen declarations of all relevant entities (if
the comma is part of the initializer, lookup may find entites declared later in
the class), we need to turn off typo-correction and diagnostics during the
tentative parse, and in the rare case that we decide the comma is part of the
initializer, we need to revert all token annotations we performed while
disambiguating.
Any diagnostics that occur outside of the immediate context of the tentative
parse (for instance, if we trigger the implicit instantiation of a class
template) are *not* suppressed, mirroring the usual rules for a SFINAE context.
llvm-svn: 190639
Diffstat (limited to 'clang/test')
| -rw-r--r-- | clang/test/Parser/cxx-ambig-init-templ.cpp | 171 | ||||
| -rw-r--r-- | clang/test/Parser/cxx-default-args.cpp | 17 | ||||
| -rw-r--r-- | clang/test/Parser/cxx0x-member-initializers.cpp | 10 |
3 files changed, 198 insertions, 0 deletions
diff --git a/clang/test/Parser/cxx-ambig-init-templ.cpp b/clang/test/Parser/cxx-ambig-init-templ.cpp new file mode 100644 index 00000000000..88ab61c3ab6 --- /dev/null +++ b/clang/test/Parser/cxx-ambig-init-templ.cpp @@ -0,0 +1,171 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +template<int> struct c { c(int) = delete; typedef void val; operator int() const; }; + +int val; +int foobar; +struct S { + int k1 = a < b < c, d > ::val, e1; + int k2 = a < b, c < d > ::val, e2; + int k3 = b < a < c, d > ::val, e3; + int k4 = b < c, x, y = d > ::val, e4; + int k5 = T1 < b, &S::operator=(int); // expected-error {{extra qualification}} + int k6 = T2 < b, &S::operator= >::val; + int k7 = T1 < b, &S::operator>(int); // expected-error {{extra qualification}} + int k8 = T2 < b, &S::operator> >::val; + int k9 = T3 < a < b, c >> (d), e5 = 1 > (e4); + int k10 = 0 < T3 < a < b, c >> (d + ) // expected-error {{expected ';' at end of declaration}} + , a > (e4); + int k11 = 0 < 1, c<3>::*ptr; + int k12 = e < 0, int a<b<c>::* >(), e11; + + void f1( + int k1 = a < b < c, d > ::val, + int k2 = b < a < c, d > ::val, + int k3 = b < c, int x = 0 > ::val, + int k4 = a < b, T3 < int > >(), // expected-error {{must be an expression}} + int k5 = a < b, c < d > ::val, + int k6 = a < b, c < d > (n) // expected-error {{undeclared identifier 'n'}} + ); + + void f2a( + // T3<int> here is a parameter type, so must be declared before it is used. + int k1 = c < b, T3 < int > x = 0 // expected-error {{unexpected end of default argument expression}} + ); + + template<typename, int=0> struct T3 { T3(int); operator int(); }; + + void f2b( + int k1 = c < b, T3 < int > x = 0 // ok + ); + + // This is a one-parameter function. Ensure we don't typo-correct it to + // int = a < b, c < foobar > () + // ... which would be a function with two parameters. + int f3(int = a < b, c < goobar > ()); + static constexpr int (S::*f3_test)(int) = &S::f3; + + void f4( + int k1 = a<1,2>::val, + int missing_default // expected-error {{missing default argument on parameter}} + ); + + void f5( + int k1 = b < c, + int missing_default // expected-error {{missing default argument on parameter}} + ); + + void f6( + int k = b < c, + unsigned int (missing_default) // expected-error {{missing default argument on parameter}} + ); + + template<int, int=0> struct a { static const int val = 0; operator int(); }; // expected-note {{here}} + static const int b = 0, c = 1, d = 2, goobar = 3; + template<int, typename> struct e { operator int(); }; + + int mp1 = 0 < 1, + a<b<c,b<c>::*mp2, + mp3 = 0 > a<b<c>::val, + a<b<c,b<c>::*mp4 = 0, + a<b<c,b<c>::*mp5 {0}, + a<b<c,b<c>::*mp6; + + int np1 = e<0, int a<b<c,b<c>::*>(); + + static const int T1 = 4; + template<int, int &(S::*)(int)> struct T2 { static const int val = 0; }; +}; + +namespace NoAnnotationTokens { + template<bool> struct Bool { Bool(int); }; + static const bool in_class = false; + + struct Test { + // Check we don't keep around a Bool<false> annotation token here. + int f(Bool<true> = X<Y, Bool<in_class> >(0)); + + // But it's OK if we do here. + int g(Bool<true> = Z<Y, Bool<in_class> = Bool<false>(0)); + + static const bool in_class = true; + template<int, typename U> using X = U; + static const int Y = 0, Z = 0; + }; +} + +namespace ImplicitInstantiation { + template<typename T> struct HasError { typename T::error error; }; // expected-error {{has no members}} + + struct S { + // This triggers the instantiation of the outer HasError<int> during + // disambiguation, even though it uses the inner HasError<int>. + void f(int a = X<Y, HasError<int>::Z >()); // expected-note {{in instantiation of}} + + template<typename, typename> struct X { operator int(); }; + typedef int Y; + template<typename> struct HasError { typedef int Z; }; + }; + + HasError<int> hei; +} + +namespace CWG325 { + template <int A, typename B> struct T { static int i; operator int(); }; + class C { + int Foo (int i = T<1, int>::i); + }; + + class D { + int Foo (int i = T<1, int>::i); + template <int A, typename B> struct T {static int i;}; + }; + + const int a = 0; + typedef int b; + T<a,b> c; + struct E { + int n = T<a,b>(c); + }; +} + +namespace Operators { + struct Y {}; + constexpr int operator,(const Y&, const Y&) { return 8; } + constexpr int operator>(const Y&, const Y&) { return 8; } + constexpr int operator<(const Y&, const Y&) { return 8; } + constexpr int operator>>(const Y&, const Y&) { return 8; } + + struct X { + typedef int (*Fn)(const Y&, const Y&); + + Fn a = operator,, b = operator<, c = operator>; + void f(Fn a = operator,, Fn b = operator<, Fn c = operator>); + + int k1 = T1<0, operator<, operator>, operator<>::val, l1; + int k2 = T1<0, operator>, operator,, operator,>::val, l2; + int k3 = T2<0, operator,(Y{}, Y{}), operator<(Y{}, Y{})>::val, l3; + int k4 = T2<0, operator>(Y{}, Y{}), operator,(Y{}, Y{})>::val, l4; + int k5 = T3<0, operator>>>::val, l5; + int k6 = T4<0, T3<0, operator>>>>::val, l6; + + template<int, Fn, Fn, Fn> struct T1 { enum { val }; }; + template<int, int, int> struct T2 { enum { val }; }; + template<int, Fn> struct T3 { enum { val }; }; + template<int, typename T> struct T4 : T {}; + }; +} + +namespace ElaboratedTypeSpecifiers { + struct S { + int f(int x = T<a, struct S>()); + int g(int x = T<a, class __declspec() C>()); + int h(int x = T<a, union __attribute__(()) U>()); + int i(int x = T<a, enum E>()); + int j(int x = T<a, struct S::template T<0, enum E>>()); + template <int, typename> struct T { operator int(); }; + static const int a = 0; + enum E {}; + }; +} diff --git a/clang/test/Parser/cxx-default-args.cpp b/clang/test/Parser/cxx-default-args.cpp index 7fe8474142b..36abf0d8cb3 100644 --- a/clang/test/Parser/cxx-default-args.cpp +++ b/clang/test/Parser/cxx-default-args.cpp @@ -14,3 +14,20 @@ typedef struct Inst { struct X { void f(int x = 1:); // expected-error {{unexpected end of default argument expression}} }; + +// PR13657 +struct T { + template <typename A, typename B> struct T1 { enum {V};}; + template <int A, int B> struct T2 { enum {V}; }; + template <int, int> static int func(int); + + + void f1(T1<int, int> = T1<int, int>()); + void f2(T1<int, double> = T1<int, double>(), T2<0, 5> = T2<0, 5>()); + void f3(int a = T2<0, (T1<int, int>::V > 10) ? 5 : 6>::V, bool b = 4<5 ); + void f4(bool a = 1 < 0, bool b = 2 > 0 ); + void f5(bool a = 1 > T2<0, 0>::V, bool b = T1<int,int>::V < 3, int c = 0); + void f6(bool a = T2<0,3>::V < 4, bool b = 4 > T2<0,3>::V); + void f7(bool a = T1<int, bool>::V < 3); + void f8(int = func<0,1<2>(0), int = 1<0, T1<int,int>(int) = 0); +}; diff --git a/clang/test/Parser/cxx0x-member-initializers.cpp b/clang/test/Parser/cxx0x-member-initializers.cpp index a324f974bca..43e99b13364 100644 --- a/clang/test/Parser/cxx0x-member-initializers.cpp +++ b/clang/test/Parser/cxx0x-member-initializers.cpp @@ -27,3 +27,13 @@ struct V1 { int a, b; V1() : a(), b{} {} }; + +template <typename, typename> struct T1 { enum {V};}; +template <int, int> struct T2 { enum {V};}; +struct A { + T1<int, int> a1 = T1<int, int>(), *a2 = new T1<int,int>; + T2<0,0> b1 = T2<0,0>(), b2 = T2<0,0>(), b3; + bool c1 = 1 < 2, c2 = 2 < 1, c3 = false; + bool d1 = T1<int, T1<int, int>>::V < 3, d2; + T1<int, int()> e = T1<int, int()>(); +}; |

