Ver código fonte

Disallow unexpected kinds of members in interfaces and impls. (#1318)

The syntax permitted in an `interface` is similar to, but different from, that permitted elsewhere. For example, associated constant declarations (`let T:! Type;`) with no initializers are valid in interfaces but nowhere else, and are a different kind of declaration from what a normal `let` declaration would produce both syntactically and semantically.

Because interfaces and impls permit only a very small subset of the complete set of kinds of declaration, it's simpler to list only the kinds that are permitted rather than to allow an arbitrary declaration and semantically disallow the invalid cases, and it's also likely to lead to better error messages as we won't try to parse unintended or meaningless things.
Richard Smith 3 anos atrás
pai
commit
36b9d997b7

+ 27 - 2
explorer/syntax/parser.ypp

@@ -108,6 +108,8 @@
 %type <Nonnull<FunctionDeclaration*>> function_declaration
 %type <Nonnull<AliasDeclaration*>> alias_declaration
 %type <std::vector<Nonnull<Declaration*>>> declaration_list
+%type <std::vector<Nonnull<Declaration*>>> interface_body
+%type <std::vector<Nonnull<Declaration*>>> impl_body
 %type <Nonnull<Statement*>> statement
 %type <Nonnull<If*>> if_statement
 %type <std::optional<Nonnull<Block*>>> optional_else
@@ -929,7 +931,7 @@ declaration:
       $$ = arena->New<VariableDeclaration>(context.source_loc(), $2, $4,
                                            ValueCategory::Let);
     }
-| INTERFACE identifier type_params LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
+| INTERFACE identifier type_params LEFT_CURLY_BRACE interface_body RIGHT_CURLY_BRACE
     {
       // TODO: Type of `Self` should be the interface being declared, not
       // `Type`.
@@ -940,7 +942,7 @@ declaration:
       $$ = arena->New<InterfaceDeclaration>(context.source_loc(), $2, $3, self,
                                             $5);
     }
-| impl_kind IMPL impl_deduced_params impl_type AS expression LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
+| impl_kind IMPL impl_deduced_params impl_type AS expression LEFT_CURLY_BRACE impl_body RIGHT_CURLY_BRACE
     {
       ErrorOr<ImplDeclaration*> impl = ImplDeclaration::Create(
           arena, context.source_loc(), $1, $4, $6, $3, $8);
@@ -974,4 +976,27 @@ declaration_list:
       $$.push_back(Nonnull<Declaration*>($2));
     }
 ;
+interface_body:
+  // Empty
+    { $$ = {}; }
+| interface_body function_declaration
+    {
+      $$ = $1;
+      $$.push_back($2);
+    }
+;
+impl_body:
+  // Empty
+    { $$ = {}; }
+| impl_body function_declaration
+    {
+      $$ = $1;
+      $$.push_back($2);
+    }
+| impl_body alias_declaration
+    {
+      $$ = $1;
+      $$.push_back($2);
+    }
+;
 %%

+ 23 - 0
explorer/testdata/impl/fail_bad_member_kind.carbon

@@ -0,0 +1,23 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{not} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+interface A {
+  // TODO: Use `let T:! Type;` here once we support it.
+  fn T();
+}
+
+external impl i32 as A {
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/impl/fail_bad_member_kind.carbon:[[@LINE+1]]: syntax error, unexpected CLASS, expecting ALIAS or FN or RIGHT_CURLY_BRACE
+  class T {}
+}
+
+fn Main() -> i32 { return 0; }

+ 18 - 0
explorer/testdata/interface/fail_bad_member_kind.carbon

@@ -0,0 +1,18 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{not} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+interface Bad {
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_bad_member_kind.carbon:[[@LINE+1]]: syntax error, unexpected VAR, expecting FN or RIGHT_CURLY_BRACE
+  var V: i32;
+}
+
+fn Main() -> i32 { return 0; }