Go patch committed: Permit compiler directives in parenthesized groups

Ian Lance Taylor iant@golang.org
Tue Oct 26 23:11:39 GMT 2021


This patch to the Go frontend permits compiler directives in
parenthesized groups.  The original compiler directive support was
only for //line at the start of a line and for //go: comments before
function declarations.  When support was added for //go:notinheap for
types and //go:embed for variables the code did not adapt to permit
spaces before the comment or to permit the comments in var() or type()
groups.  This change corrects those omissions.  Bootstrapped and ran
Go testsuite on x86_64-pc-linux-gnu.  Committed to mainline.

Ian
-------------- next part --------------
4571c4689239eda7567a69b0decea52c5f6ce501
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index affba73e4bb..e7ff6705563 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-925ace70ac7426c3f8b5c0bfb75aa9601f071de4
+128ea3dce9b8753167f33d0a96bd093a6cbd58b8
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc
index dd66c0209a4..d6909b840a8 100644
--- a/gcc/go/gofrontend/lex.cc
+++ b/gcc/go/gofrontend/lex.cc
@@ -1909,14 +1909,13 @@ Lex::skip_cpp_comment()
   if (saw_error)
     return;
 
-  // Recognize various magic comments at the start of a line.
+  // Recognize various magic comments at the start of a line, preceded
+  // only by spaces or tabs.
 
-  if (lineoff != 2)
-    {
-      // Not at the start of the line.  (lineoff == 2 because of the
-      // two characters in "//").
+  // "- 2" for the "//" at the start of the comment.
+  for (const char* psp = this->linebuf_; psp < p - 2; psp++)
+    if (*psp != ' ' && *psp != '\t')
       return;
-    }
 
   while (pend > p
 	 && (pend[-1] == ' ' || pend[-1] == '\t'
diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h
index 75c8429b68f..1c4cc5bd2a1 100644
--- a/gcc/go/gofrontend/lex.h
+++ b/gcc/go/gofrontend/lex.h
@@ -420,6 +420,12 @@ class Lex
     std::swap(*embeds, this->embeds_);
   }
 
+  // Clear any go:embed patterns seen so far.  This is used for
+  // erroneous cases.
+  void
+  clear_embeds()
+  { this->embeds_.clear(); }
+
   // Return whether the identifier NAME should be exported.  NAME is a
   // mangled name which includes only ASCII characters.
   static bool
diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc
index e43b5f21448..cc197e5eb35 100644
--- a/gcc/go/gofrontend/parse.cc
+++ b/gcc/go/gofrontend/parse.cc
@@ -1307,46 +1307,14 @@ void
 Parse::declaration()
 {
   const Token* token = this->peek_token();
-
-  unsigned int pragmas = this->lex_->get_and_clear_pragmas();
-  if (pragmas != 0
-      && !token->is_keyword(KEYWORD_FUNC)
-      && !token->is_keyword(KEYWORD_TYPE))
-    go_warning_at(token->location(), 0,
-		  "ignoring magic comment before non-function");
-
-  std::vector<std::string>* embeds = NULL;
-  if (this->lex_->has_embeds())
-    {
-      embeds = new(std::vector<std::string>);
-      this->lex_->get_and_clear_embeds(embeds);
-
-      if (!this->gogo_->current_file_imported_embed())
-	{
-	  go_error_at(token->location(),
-		      "invalid go:embed: missing import %<embed%>");
-	  delete embeds;
-	  embeds = NULL;
-	}
-      if (!token->is_keyword(KEYWORD_VAR))
-	{
-	  go_error_at(token->location(), "misplaced go:embed directive");
-	  if (embeds != NULL)
-	    {
-	      delete embeds;
-	      embeds = NULL;
-	    }
-	}
-    }
-
   if (token->is_keyword(KEYWORD_CONST))
     this->const_decl();
   else if (token->is_keyword(KEYWORD_TYPE))
-    this->type_decl(pragmas);
+    this->type_decl();
   else if (token->is_keyword(KEYWORD_VAR))
-    this->var_decl(embeds);
+    this->var_decl();
   else if (token->is_keyword(KEYWORD_FUNC))
-    this->function_decl(pragmas);
+    this->function_decl();
   else
     {
       go_error_at(this->location(), "expected declaration");
@@ -1367,8 +1335,7 @@ Parse::declaration_may_start_here()
 // Decl<P> = P | "(" [ List<P> ] ")" .
 
 void
-Parse::decl(void (Parse::*pfn)(unsigned int, std::vector<std::string>*),
-	    unsigned int pragmas, std::vector<std::string>* embeds)
+Parse::decl(void (Parse::*pfn)())
 {
   if (this->peek_token()->is_eof())
     {
@@ -1378,15 +1345,18 @@ Parse::decl(void (Parse::*pfn)(unsigned int, std::vector<std::string>*),
     }
 
   if (!this->peek_token()->is_op(OPERATOR_LPAREN))
-    (this->*pfn)(pragmas, embeds);
+    (this->*pfn)();
   else
     {
-      if (pragmas != 0)
-	go_warning_at(this->location(), 0,
-		      "ignoring magic %<//go:...%> comment before group");
-      if (embeds != NULL)
+      if (this->lex_->get_and_clear_pragmas() != 0)
 	go_error_at(this->location(),
-		    "ignoring %<//go:embed%> comment before group");
+		    "ignoring compiler directive before group");
+      if (this->lex_->has_embeds())
+	{
+	  this->lex_->clear_embeds();
+	  go_error_at(this->location(),
+		      "ignoring %<//go:embed%> comment before group");
+	}
       if (!this->advance_token()->is_op(OPERATOR_RPAREN))
 	{
 	  this->list(pfn, true);
@@ -1410,10 +1380,9 @@ Parse::decl(void (Parse::*pfn)(unsigned int, std::vector<std::string>*),
 // might follow.  This is either a '}' or a ')'.
 
 void
-Parse::list(void (Parse::*pfn)(unsigned int, std::vector<std::string>*),
-	    bool follow_is_paren)
+Parse::list(void (Parse::*pfn)(), bool follow_is_paren)
 {
-  (this->*pfn)(0, NULL);
+  (this->*pfn)();
   Operator follow = follow_is_paren ? OPERATOR_RPAREN : OPERATOR_RCURLY;
   while (this->peek_token()->is_op(OPERATOR_SEMICOLON)
 	 || this->peek_token()->is_op(OPERATOR_COMMA))
@@ -1422,7 +1391,7 @@ Parse::list(void (Parse::*pfn)(unsigned int, std::vector<std::string>*),
 	go_error_at(this->location(), "unexpected comma");
       if (this->advance_token()->is_op(follow))
 	break;
-      (this->*pfn)(0, NULL);
+      (this->*pfn)();
     }
 }
 
@@ -1469,6 +1438,8 @@ Parse::const_decl()
 void
 Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list)
 {
+  this->check_directives();
+
   Location loc = this->location();
   Typed_identifier_list til;
   this->identifier_list(&til);
@@ -1545,18 +1516,21 @@ Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list)
 // TypeDecl = "type" Decl<TypeSpec> .
 
 void
-Parse::type_decl(unsigned int pragmas)
+Parse::type_decl()
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_TYPE));
   this->advance_token();
-  this->decl(&Parse::type_spec, pragmas, NULL);
+  this->decl(&Parse::type_spec);
 }
 
 // TypeSpec = identifier ["="] Type .
 
 void
-Parse::type_spec(unsigned int pragmas, std::vector<std::string>*)
+Parse::type_spec()
 {
+  unsigned int pragmas = this->lex_->get_and_clear_pragmas();
+  this->check_directives();
+
   const Token* token = this->peek_token();
   if (!token->is_identifier())
     {
@@ -1649,23 +1623,34 @@ Parse::type_spec(unsigned int pragmas, std::vector<std::string>*)
 // VarDecl = "var" Decl<VarSpec> .
 
 void
-Parse::var_decl(std::vector<std::string>* embeds)
+Parse::var_decl()
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_VAR));
   this->advance_token();
-  this->decl(&Parse::var_spec, 0, embeds);
+  this->decl(&Parse::var_spec);
 }
 
 // VarSpec = IdentifierList
 //             ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) .
 
 void
-Parse::var_spec(unsigned int pragmas, std::vector<std::string>* embeds)
+Parse::var_spec()
 {
   Location loc = this->location();
 
-  if (pragmas != 0)
-    go_warning_at(loc, 0, "ignoring magic %<//go:...%> comment before var");
+  std::vector<std::string>* embeds = NULL;
+  if (this->lex_->has_embeds())
+    {
+      if (!this->gogo_->current_file_imported_embed())
+	go_error_at(loc, "invalid go:embed: missing import %<embed%>");
+      else
+	{
+	  embeds = new(std::vector<std::string>);
+	  this->lex_->get_and_clear_embeds(embeds);
+	}
+    }
+
+  this->check_directives();
 
   // Get the variable names.
   Typed_identifier_list til;
@@ -2339,9 +2324,13 @@ Parse::simple_var_decl_or_assignment(const std::string& name,
 // PRAGMAS is a bitset of magic comments.
 
 void
-Parse::function_decl(unsigned int pragmas)
+Parse::function_decl()
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC));
+
+  unsigned int pragmas = this->lex_->get_and_clear_pragmas();
+  this->check_directives();
+
   Location location = this->location();
   std::string extern_name = this->lex_->extern_name();
   const Token* token = this->advance_token();
@@ -5370,7 +5359,7 @@ Parse::for_stat(Label* label)
 	{
 	  go_error_at(this->location(),
                       "var declaration not allowed in for initializer");
-	  this->var_decl(NULL);
+	  this->var_decl();
 	}
 
       if (token->is_op(OPERATOR_SEMICOLON))
@@ -5815,17 +5804,15 @@ Parse::import_decl()
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_IMPORT));
   this->advance_token();
-  this->decl(&Parse::import_spec, 0, NULL);
+  this->decl(&Parse::import_spec);
 }
 
 // ImportSpec = [ "." | PackageName ] PackageFileName .
 
 void
-Parse::import_spec(unsigned int pragmas, std::vector<std::string>*)
+Parse::import_spec()
 {
-  if (pragmas != 0)
-    go_warning_at(this->location(), 0,
-		  "ignoring magic %<//go:...%> comment before import");
+  this->check_directives();
 
   const Token* token = this->peek_token();
   Location location = token->location();
@@ -5916,6 +5903,23 @@ Parse::program()
 	  this->skip_past_error(OPERATOR_INVALID);
 	}
     }
+
+  this->check_directives();
+}
+
+// If there are any pending compiler directives, clear them and give
+// an error.  This is called when directives are not permitted.
+
+void
+Parse::check_directives()
+{
+  if (this->lex_->get_and_clear_pragmas() != 0)
+    go_error_at(this->location(), "misplaced compiler directive");
+  if (this->lex_->has_embeds())
+    {
+      this->lex_->clear_embeds();
+      go_error_at(this->location(), "misplaced go:embed directive");
+    }
 }
 
 // Skip forward to a semicolon or OP.  OP will normally be
diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h
index 2c3c505ffa9..6e300ef800c 100644
--- a/gcc/go/gofrontend/parse.h
+++ b/gcc/go/gofrontend/parse.h
@@ -181,15 +181,14 @@ class Parse
   void method_spec(Typed_identifier_list*);
   void declaration();
   bool declaration_may_start_here();
-  void decl(void (Parse::*)(unsigned int, std::vector<std::string>*),
-	    unsigned int pragmas, std::vector<std::string>* embeds);
-  void list(void (Parse::*)(unsigned int, std::vector<std::string>*), bool);
+  void decl(void (Parse::*)());
+  void list(void (Parse::*)(), bool);
   void const_decl();
   void const_spec(int, Type**, Expression_list**);
-  void type_decl(unsigned int pragmas);
-  void type_spec(unsigned int pragmas, std::vector<std::string>*);
-  void var_decl(std::vector<std::string>* embeds);
-  void var_spec(unsigned int pragmas, std::vector<std::string>*);
+  void type_decl();
+  void type_spec();
+  void var_decl();
+  void var_spec();
   void init_vars(const Typed_identifier_list*, Type*, Expression_list*,
 		 bool is_coloneq, std::vector<std::string>*, Location);
   bool init_vars_from_call(const Typed_identifier_list*, Type*, Expression*,
@@ -210,7 +209,7 @@ class Parse
   void simple_var_decl_or_assignment(const std::string&, Location,
 				     bool may_be_composite_lit,
 				     Range_clause*, Type_switch*);
-  void function_decl(unsigned int pragmas);
+  void function_decl();
   Typed_identifier* receiver();
   Expression* operand(bool may_be_sink, bool *is_parenthesized);
   Expression* enclosing_var_reference(Named_object*, Named_object*,
@@ -278,7 +277,10 @@ class Parse
   void goto_stat();
   void package_clause();
   void import_decl();
-  void import_spec(unsigned int pragmas, std::vector<std::string>*);
+  void import_spec();
+
+  // Check for unused compiler directives.
+  void check_directives();
 
   // Skip past an error looking for a semicolon or OP.  Return true if
   // all is well, false if we found EOF.


More information about the Gcc-patches mailing list