sammccall created this revision.
sammccall added a reviewer: hokein.
Herald added a project: All.
sammccall requested review of this revision.
Herald added subscribers: cfe-commits, alextsao1999.
Herald added a project: clang-tools-extra.

Motivating case: `foo bar;` is not a declaration of nothing with `foo` and `bar`
both types.

This is a common and critical ambiguity, and I expect eliminating it in the
grammar is more efficient than using guards (without caching).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130150

Files:
  clang-tools-extra/pseudo/lib/cxx/cxx.bnf
  clang-tools-extra/pseudo/test/cxx/decl-specifier-seq.cpp
  clang-tools-extra/pseudo/test/glr.cpp

Index: clang-tools-extra/pseudo/test/glr.cpp
===================================================================
--- clang-tools-extra/pseudo/test/glr.cpp
+++ clang-tools-extra/pseudo/test/glr.cpp
@@ -11,12 +11,12 @@
 // CHECK-NEXT: │ │   └─unqualified-id~IDENTIFIER := tok[7]
 // CHECK-NEXT: │ └─; := tok[8]
 // CHECK-NEXT: └─statement~simple-declaration := decl-specifier-seq init-declarator-list ;
-// CHECK-NEXT:   ├─decl-specifier-seq~simple-type-specifier := <ambiguous>
-// CHECK-NEXT:   │ ├─simple-type-specifier~type-name := <ambiguous>
+// CHECK-NEXT:   ├─decl-specifier-seq~exclusive-simple-type-specifier := <ambiguous>
+// CHECK-NEXT:   │ ├─exclusive-simple-type-specifier~type-name := <ambiguous>
 // CHECK-NEXT:   │ │ ├─type-name~IDENTIFIER := tok[5]
 // CHECK-NEXT:   │ │ ├─type-name~IDENTIFIER := tok[5]
 // CHECK-NEXT:   │ │ └─type-name~IDENTIFIER := tok[5]
-// CHECK-NEXT:   │ └─simple-type-specifier~IDENTIFIER := tok[5]
+// CHECK-NEXT:   │ └─exclusive-simple-type-specifier~IDENTIFIER := tok[5]
 // CHECK-NEXT:   ├─init-declarator-list~ptr-declarator := ptr-operator ptr-declarator
 // CHECK-NEXT:   │ ├─ptr-operator~* := tok[6]
 // CHECK-NEXT:   │ └─ptr-declarator~id-expression =#1
@@ -24,7 +24,7 @@
 }
 
 // CHECK:      3 Ambiguous nodes:
-// CHECK-NEXT: 1 simple-type-specifier
+// CHECK-NEXT: 1 exclusive-simple-type-specifier
 // CHECK-NEXT: 1 statement
 // CHECK-NEXT: 1 type-name
 // CHECK-EMPTY:
Index: clang-tools-extra/pseudo/test/cxx/decl-specifier-seq.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/pseudo/test/cxx/decl-specifier-seq.cpp
@@ -0,0 +1,34 @@
+// RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s
+// No ambiguity: 'bar' is definitely a variable name.
+const foo volatile bar;
+// CHECK:      simple-declaration
+// CHECK-NEXT: ├─decl-specifier-seq
+// CHECK-NEXT: │ ├─combining-decl-specifier-seq~CONST
+// CHECK-NEXT: │ ├─exclusive-defining-type-specifier~exclusive-simple-type-specifier := <ambiguous>
+// CHECK-NEXT: │ │ ├─exclusive-simple-type-specifier~type-name := <ambiguous>
+// CHECK-NEXT: │ │ │ ├─type-name~IDENTIFIER
+// CHECK-NEXT: │ │ │ ├─type-name~IDENTIFIER
+// CHECK-NEXT: │ │ │ └─type-name~IDENTIFIER
+// CHECK-NEXT: │ │ └─exclusive-simple-type-specifier~IDENTIFIER
+// CHECK-NEXT: │ └─combining-decl-specifier-seq~VOLATILE
+// CHECK-NEXT: ├─init-declarator-list~IDENTIFIER
+// CHECK-NEXT: └─; := tok[4]
+// Ambiguity: though unlikely, we may be declaring nothing.
+unsigned baz;
+// CHECK-NEXT: declaration~simple-declaration := <ambiguous>
+// CHECK-NEXT: ├─simple-declaration := decl-specifier-seq ;
+// CHECK-NEXT: ├─decl-specifier-seq
+// CHECK-NEXT: │ │ ├─combining-decl-specifier-seq := combining-decl-specifier #1
+// CHECK-NEXT: │ │ │ └─combining-decl-specifier~UNSIGNED
+// CHECK-NEXT: │ │ └─exclusive-defining-type-specifier~exclusive-simple-type-specifier
+// CHECK-NEXT: │ │   ├─exclusive-simple-type-specifier~type-name
+// CHECK-NEXT: │ │   │ ├─type-name~IDENTIFIER
+// CHECK-NEXT: │ │   │ ├─type-name~IDENTIFIER
+// CHECK-NEXT: │ │   │ └─type-name~IDENTIFIER
+// CHECK-NEXT: │ │   └─exclusive-simple-type-specifier~IDENTIFIER
+// CHECK-NEXT: │ └─;
+// CHECK-NEXT: └─simple-declaration := decl-specifier-seq init-declarator-list ;
+// CHECK-NEXT:   ├─decl-specifier-seq~combining-decl-specifier-seq =#1
+// CHECK-NEXT:   ├─init-declarator-list~IDENTIFIER
+// CHECK-NEXT:   └─;
+
Index: clang-tools-extra/pseudo/lib/cxx/cxx.bnf
===================================================================
--- clang-tools-extra/pseudo/lib/cxx/cxx.bnf
+++ clang-tools-extra/pseudo/lib/cxx/cxx.bnf
@@ -340,17 +340,28 @@
 static_assert-declaration := STATIC_ASSERT ( constant-expression , string-literal ) ;
 empty-declaration := ;
 # dcl.spec
-decl-specifier := storage-class-specifier
-decl-specifier := defining-type-specifier
-decl-specifier := function-specifier
-decl-specifier := FRIEND
-decl-specifier := TYPEDEF
-decl-specifier := CONSTEXPR
-decl-specifier := CONSTEVAL
-decl-specifier := CONSTINIT
-decl-specifier := INLINE
-decl-specifier-seq := decl-specifier
-decl-specifier-seq := decl-specifier decl-specifier-seq
+#! Standard grammar doesn't require a {decl,type,defining-type}-specifier-seq
+#! to have only one e.g. class name.
+#! This leads to ambiguity: does `foo bar;` declare:
+#!  - a variable named `bar` of type `foo`?
+#!  - nothing, with both `foo` and `bar` being types?!
+#! Here we eliminate that ambiguity in the grammar:
+#!  exclusive-* has a type name (e.g exclusive-type-specifier ~ IDENTIFIER)
+#!  combining-* has no type name (e.g. combining-type-specifier ~ UNSIGNED)
+#!  combining-*-seq has no type name (e.g. UNSIGNED CONST)
+#!  *-seq has at most one type name (e.g. type-specifier-seq ~ CONST IDENTIFIER)
+combining-decl-specifier := storage-class-specifier
+combining-decl-specifier := combining-defining-type-specifier
+combining-decl-specifier := function-specifier
+combining-decl-specifier := FRIEND
+combining-decl-specifier := TYPEDEF
+combining-decl-specifier := CONSTEXPR
+combining-decl-specifier := CONSTEVAL
+combining-decl-specifier := CONSTINIT
+combining-decl-specifier := INLINE
+combining-decl-specifier-seq := combining-decl-specifier combining-decl-specifier-seq_opt
+decl-specifier-seq := combining-decl-specifier-seq
+decl-specifier-seq := combining-decl-specifier-seq_opt exclusive-defining-type-specifier combining-decl-specifier-seq_opt
 storage-class-specifier := STATIC
 storage-class-specifier := THREAD_LOCAL
 storage-class-specifier := EXTERN
@@ -359,36 +370,42 @@
 function-specifier := explicit-specifier
 explicit-specifier := EXPLICIT ( constant-expression )
 explicit-specifier := EXPLICIT
-type-specifier := simple-type-specifier
-type-specifier := elaborated-type-specifier
-type-specifier := typename-specifier
-type-specifier := cv-qualifier
-type-specifier-seq := type-specifier
-type-specifier-seq := type-specifier type-specifier-seq
-defining-type-specifier := type-specifier
-defining-type-specifier := class-specifier
-defining-type-specifier := enum-specifier
-defining-type-specifier-seq := defining-type-specifier
-defining-type-specifier-seq := defining-type-specifier defining-type-specifier-seq
-simple-type-specifier := nested-name-specifier_opt type-name
-simple-type-specifier := nested-name-specifier TEMPLATE simple-template-id
-simple-type-specifier := decltype-specifier
-simple-type-specifier := placeholder-type-specifier
-simple-type-specifier := nested-name-specifier_opt template-name
-simple-type-specifier := CHAR
-simple-type-specifier := CHAR8_T
-simple-type-specifier := CHAR16_T
-simple-type-specifier := CHAR32_T
-simple-type-specifier := WCHAR_T
-simple-type-specifier := BOOL
-simple-type-specifier := SHORT
-simple-type-specifier := INT
-simple-type-specifier := LONG
-simple-type-specifier := SIGNED
-simple-type-specifier := UNSIGNED
-simple-type-specifier := FLOAT
-simple-type-specifier := DOUBLE
-simple-type-specifier := VOID
+combining-type-specifier := combining-simple-type-specifier
+exclusive-type-specifier := exclusive-simple-type-specifier
+exclusive-type-specifier := elaborated-type-specifier
+exclusive-type-specifier := typename-specifier
+combining-type-specifier := cv-qualifier
+combining-type-specifier-seq := combining-type-specifier combining-type-specifier-seq_opt
+type-specifier-seq := combining-type-specifier-seq
+type-specifier-seq := combining-type-specifier_opt exclusive-type-specifier combining-type-specifier_opt
+combining-defining-type-specifier := combining-type-specifier
+exclusive-defining-type-specifier := exclusive-type-specifier
+exclusive-defining-type-specifier := class-specifier
+exclusive-defining-type-specifier := enum-specifier
+combining-defining-type-specifier-seq := combining-defining-type-specifier combining-defining-type-specifier-seq_opt
+defining-type-specifier-seq := combining-defining-type-specifier-seq
+defining-type-specifier-seq := combining-defining-type-specifier-seq_opt exclusive-type-specifier combining-defining-type-specifier-seq_opt
+simple-type-specifier := exclusive-simple-type-specifier
+simple-type-specifier := combining-simple-type-specifier
+exclusive-simple-type-specifier := nested-name-specifier_opt type-name
+exclusive-simple-type-specifier := nested-name-specifier TEMPLATE simple-template-id
+exclusive-simple-type-specifier := decltype-specifier
+exclusive-simple-type-specifier := placeholder-type-specifier
+exclusive-simple-type-specifier := nested-name-specifier_opt template-name
+exclusive-simple-type-specifier := CHAR
+exclusive-simple-type-specifier := CHAR8_T
+exclusive-simple-type-specifier := CHAR16_T
+exclusive-simple-type-specifier := CHAR32_T
+exclusive-simple-type-specifier := WCHAR_T
+exclusive-simple-type-specifier := BOOL
+combining-simple-type-specifier := SHORT
+exclusive-simple-type-specifier := INT
+combining-simple-type-specifier := LONG
+combining-simple-type-specifier := SIGNED
+combining-simple-type-specifier := UNSIGNED
+exclusive-simple-type-specifier := FLOAT
+exclusive-simple-type-specifier := DOUBLE
+exclusive-simple-type-specifier := VOID
 type-name := class-name
 type-name := enum-name
 type-name := typedef-name
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to