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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits