PR45142: 'template ~X<T>' is ill-formed; reject it rather than crashing.
authorRichard Smith <richard@metafoo.co.uk>
Mon, 23 Mar 2020 22:05:38 +0000 (15:05 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Mon, 23 Mar 2020 22:07:06 +0000 (15:07 -0700)
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/lib/Parse/ParseExprCXX.cpp
clang/test/CXX/drs/dr4xx.cpp
clang/test/SemaCXX/pseudo-destructors.cpp

index acb2292..dcbb0d3 100644 (file)
@@ -706,6 +706,8 @@ def err_id_after_template_in_nested_name_spec : Error<
   "expected template name after 'template' keyword in nested name specifier">;
 def err_unexpected_template_in_unqualified_id : Error<
   "'template' keyword not permitted here">;
+def err_unexpected_template_in_destructor_name : Error<
+  "'template' keyword not permitted in destructor name">;
 def err_unexpected_template_after_using : Error<
   "'template' keyword not permitted after 'using' keyword">;
 def err_two_right_angle_brackets_need_space : Error<
index b8d91c1..a0b97ea 100644 (file)
@@ -2884,6 +2884,22 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
     // Parse the '~'.
     SourceLocation TildeLoc = ConsumeToken();
 
+    if (TemplateSpecified) {
+      // C++ [temp.names]p3:
+      //   A name prefixed by the keyword template shall be a template-id [...]
+      //
+      // A template-id cannot begin with a '~' token. This would never work
+      // anyway: x.~A<int>() would specify that the destructor is a template,
+      // not that 'A' is a template.
+      //
+      // FIXME: Suggest replacing the attempted destructor name with a correct
+      // destructor name and recover. (This is not trivial if this would become
+      // a pseudo-destructor name).
+      Diag(*TemplateKWLoc, diag::err_unexpected_template_in_destructor_name)
+        << Tok.getLocation();
+      return true;
+    }
+
     if (SS.isEmpty() && Tok.is(tok::kw_decltype)) {
       DeclSpec DS(AttrFactory);
       SourceLocation EndLoc = ParseDecltypeSpecifier(DS);
@@ -2903,7 +2919,7 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
 
     // If the user wrote ~T::T, correct it to T::~T.
     DeclaratorScopeObj DeclScopeObj(*this, SS);
-    if (!TemplateSpecified && NextToken().is(tok::coloncolon)) {
+    if (NextToken().is(tok::coloncolon)) {
       // Don't let ParseOptionalCXXScopeSpecifier() "correct"
       // `int A; struct { ~A::A(); };` to `int A; struct { ~A:A(); };`,
       // it will confuse this recovery logic.
index 35fd15f..2c76223 100644 (file)
@@ -297,13 +297,11 @@ namespace dr420 { // dr420: yes
   void test2(T p) {
     p->template Y<int>::~Y<int>();
     p->~Y<int>();
-    // FIXME: This is ill-formed, but this diagnostic is terrible. We should
-    // reject this in the parser.
-    p->template ~Y<int>(); // expected-error 2{{no member named '~typename Y<int>'}}
+    p->template ~Y<int>(); // expected-error {{'template' keyword not permitted in destructor name}}
   }
   template<typename T> struct Y {};
-  template void test2(Y<int>*); // expected-note {{instantiation}}
-  template void test2(ptr<Y<int> >); // expected-note {{instantiation}}
+  template void test2(Y<int>*);
+  template void test2(ptr<Y<int> >);
 
   void test3(int *p, ptr<int> q) {
     typedef int Int;
index 0cd1390..b71b523 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -emit-llvm-only -verify -std=c++11 %s
 struct A {};
 
 enum Foo { F };
@@ -92,6 +92,9 @@ namespace PR11339 {
 template<typename T> using Id = T;
 void AliasTemplate(int *p) {
   p->~Id<int>();
+  p->template ~Id<int>(); // expected-error {{'template' keyword not permitted in destructor name}}
+  (0).~Id<int>();
+  (0).template ~Id<int>(); // expected-error {{'template' keyword not permitted in destructor name}}
 }
 
 namespace dotPointerAccess {