This patch to the Go frontend reads go:embed directives and attaches
them to variables.  It also reads the embedcfg file passed on the
command line.  We still don't actually do anything with the
directives.  Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.
Committed to mainline.

Ian
f6b3e2c2f626e9a84a3e37bc60bdb133bbd2a6e0
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 82f43f5f21f..fb4ec30913e 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-22ce16e28220d446c4557f47129024e3561f3d77
+9e78cef2b689aa586dbf677fb47ea3f08f197b91
 
 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/embed.cc b/gcc/go/gofrontend/embed.cc
index 19c6930d0c3..7ee86746212 100644
--- a/gcc/go/gofrontend/embed.cc
+++ b/gcc/go/gofrontend/embed.cc
@@ -626,3 +626,18 @@ Embedcfg_reader::error(const char* msg)
                "%<-fgo-embedcfg%>: %s: %s",
                this->filename_, msg);
 }
+
+// Return whether the current file imports "embed".
+
+bool
+Gogo::is_embed_imported() const
+{
+  Packages::const_iterator p = this->packages_.find("embed");
+  if (p == this->packages_.end())
+    return false;
+
+  // We track current file imports in the package aliases, where a
+  // typical import will just list the package name in aliases.  So
+  // the package has been imported if there is at least one alias.
+  return !p->second->aliases().empty();
+}
diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc
index e026d6592ba..404cb124549 100644
--- a/gcc/go/gofrontend/go.cc
+++ b/gcc/go/gofrontend/go.cc
@@ -40,6 +40,8 @@ go_create_gogo(const struct go_create_gogo_args* args)
     ::gogo->set_compiling_runtime(args->compiling_runtime);
   if (args->c_header != NULL)
     ::gogo->set_c_header(args->c_header);
+  if (args->embedcfg != NULL)
+    ::gogo->read_embedcfg(args->embedcfg);
   ::gogo->set_debug_escape_level(args->debug_escape_level);
   if (args->debug_escape_hash != NULL)
     ::gogo->set_debug_escape_hash(args->debug_escape_hash);
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index fbf8935bb06..4c795a2b495 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -7456,8 +7456,8 @@ Variable::Variable(Type* type, Expression* init, bool 
is_global,
                   bool is_parameter, bool is_receiver,
                   Location location)
   : type_(type), init_(init), preinit_(NULL), location_(location),
-    backend_(NULL), is_global_(is_global), is_parameter_(is_parameter),
-    is_closure_(false), is_receiver_(is_receiver),
+    embeds_(NULL), backend_(NULL), is_global_(is_global),
+    is_parameter_(is_parameter), is_closure_(false), is_receiver_(is_receiver),
     is_varargs_parameter_(false), is_global_sink_(false), is_used_(false),
     is_address_taken_(false), is_non_escaping_address_taken_(false),
     seen_(false), init_is_lowered_(false), init_is_flattened_(false),
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index 0d80bdeda4f..891ef697ffe 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -397,6 +397,10 @@ class Gogo
   void
   read_embedcfg(const char* filename);
 
+  // Return whether the current file imports "embed".
+  bool
+  is_embed_imported() const;
+
   // Return whether to check for division by zero in binary operations.
   bool
   check_divide_by_zero() const
@@ -2276,6 +2280,16 @@ class Variable
     this->is_referenced_by_inline_ = true;
   }
 
+  // Attach any go:embed comments for this variable.
+  void
+  set_embeds(std::vector<std::string>* embeds)
+  {
+    go_assert(this->is_global_
+             && this->init_ == NULL
+             && this->preinit_ == NULL);
+    this->embeds_ = embeds;
+  }
+
   // Return the top-level declaration for this variable.
   Statement*
   toplevel_decl()
@@ -2346,6 +2360,8 @@ class Variable
   Block* preinit_;
   // Location of variable definition.
   Location location_;
+  // Any associated go:embed comments.
+  std::vector<std::string>* embeds_;
   // Backend representation.
   Bvariable* backend_;
   // Whether this is a global variable.
diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc
index 0baf4e4e24b..dd66c0209a4 100644
--- a/gcc/go/gofrontend/lex.cc
+++ b/gcc/go/gofrontend/lex.cc
@@ -2035,6 +2035,8 @@ Lex::skip_cpp_comment()
          (*this->linknames_)[go_name] = Linkname(ext_name, is_exported, loc);
        }
     }
+  else if (verb == "go:embed")
+    this->gather_embed(ps, pend);
   else if (verb == "go:nointerface")
     {
       // For field tracking analysis: a //go:nointerface comment means
@@ -2111,6 +2113,98 @@ Lex::skip_cpp_comment()
     }
 }
 
+// Read a go:embed directive.  This is a series of space-separated
+// patterns.  Each pattern may be a quoted or backquoted string.
+
+void
+Lex::gather_embed(const char *p, const char *pend)
+{
+  while (true)
+    {
+      // Skip spaces to find the start of the next pattern.  We do a
+      // fast skip of space and tab, but we also permit and skip
+      // Unicode space characters.
+      while (p < pend && (*p == ' ' || *p == '\t'))
+       ++p;
+      if (p >= pend)
+       break;
+      unsigned int c;
+      bool issued_error;
+      const char *pnext = this->advance_one_utf8_char(p, &c, &issued_error);
+      if (issued_error)
+       return;
+      if (Lex::is_unicode_space(c))
+       {
+         p = pnext;
+         continue;
+       }
+
+      // Here P points to the start of the next pattern, PNEXT points
+      // to the second character in the pattern, and C is the first
+      // character in that pattern (the character to which P points).
+
+      if (c == '"' || c == '`')
+       {
+         Location loc = this->location();
+         const unsigned char quote = c;
+         std::string value;
+         p = pnext;
+         while (p < pend && *p != quote)
+           {
+             bool is_character;
+             if (quote == '"')
+               p = this->advance_one_char(p, false, &c, &is_character);
+             else
+               {
+                 p = this->advance_one_utf8_char(p, &c, &issued_error);
+                 if (issued_error)
+                   return;
+                 // "Carriage return characters ('\r') inside raw string
+                 // literals are discarded from the raw string value."
+                 if (c == '\r')
+                   continue;
+                 is_character = true;
+               }
+             Lex::append_char(c, is_character, &value, loc);
+           }
+         if (p >= pend)
+           {
+             // Note that within a go:embed directive we do not
+             // permit raw strings to cross multiple lines.
+             go_error_at(loc, "unterminated string");
+             return;
+           }
+         this->embeds_.push_back(value);
+         ++p;
+       }
+      else
+       {
+         const char *start = p;
+         p = pnext;
+         while (p < pend)
+           {
+             c = *p;
+             if (c == ' ' || c == '\t')
+               break;
+             if (c > ' ' && c <= 0x7f)
+               {
+                 // ASCII non-space character.
+                 ++p;
+                 continue;
+               }
+             pnext = this->advance_one_utf8_char(p, &c, &issued_error);
+             if (issued_error)
+               return;
+             if (Lex::is_unicode_space(c))
+               break;
+             p = pnext;
+           }
+
+         this->embeds_.push_back(std::string(start, p - start));
+       }
+    }
+}
+
 // The Unicode tables use this struct.
 
 struct Unicode_range
diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h
index 3be38062150..75c8429b68f 100644
--- a/gcc/go/gofrontend/lex.h
+++ b/gcc/go/gofrontend/lex.h
@@ -405,6 +405,21 @@ class Lex
     return ret;
   }
 
+  // Return whether there are any current go:embed patterns.
+  bool
+  has_embeds() const
+  { return !this->embeds_.empty(); }
+
+  // If there are any go:embed patterns seen so far, store them in
+  // *EMBEDS and clear the saved set.  *EMBEDS must be an empty
+  // vector.
+  void
+  get_and_clear_embeds(std::vector<std::string>* embeds)
+  {
+    go_assert(embeds->empty());
+    std::swap(*embeds, this->embeds_);
+  }
+
   // Return whether the identifier NAME should be exported.  NAME is a
   // mangled name which includes only ASCII characters.
   static bool
@@ -536,6 +551,9 @@ class Lex
   void
   skip_cpp_comment();
 
+  void
+  gather_embed(const char*, const char*);
+
   // The input file name.
   const char* input_file_name_;
   // The input file.
@@ -561,6 +579,8 @@ class Lex
   std::string extern_;
   // The list of //go:linkname comments, if any.
   Linknames* linknames_;
+  // The list of //go:embed patterns, if any.
+  std::vector<std::string> embeds_;
 };
 
 #endif // !defined(GO_LEX_H)
diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc
index 1664fe36c28..fd81a85c87f 100644
--- a/gcc/go/gofrontend/parse.cc
+++ b/gcc/go/gofrontend/parse.cc
@@ -1315,12 +1315,36 @@ Parse::declaration()
     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_->is_embed_imported())
+       {
+         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);
   else if (token->is_keyword(KEYWORD_VAR))
-    this->var_decl();
+    this->var_decl(embeds);
   else if (token->is_keyword(KEYWORD_FUNC))
     this->function_decl(pragmas);
   else
@@ -1343,8 +1367,8 @@ Parse::declaration_may_start_here()
 // Decl<P> = P | "(" [ List<P> ] ")" .
 
 void
-Parse::decl(void (Parse::*pfn)(void*, unsigned int), void* varg,
-           unsigned int pragmas)
+Parse::decl(void (Parse::*pfn)(unsigned int, std::vector<std::string>*),
+           unsigned int pragmas, std::vector<std::string>* embeds)
 {
   if (this->peek_token()->is_eof())
     {
@@ -1354,15 +1378,18 @@ Parse::decl(void (Parse::*pfn)(void*, unsigned int), 
void* varg,
     }
 
   if (!this->peek_token()->is_op(OPERATOR_LPAREN))
-    (this->*pfn)(varg, pragmas);
+    (this->*pfn)(pragmas, embeds);
   else
     {
       if (pragmas != 0)
        go_warning_at(this->location(), 0,
                      "ignoring magic %<//go:...%> comment before group");
+      if (embeds != NULL)
+       go_error_at(this->location(),
+                   "ignoring %<//go:embed%> comment before group");
       if (!this->advance_token()->is_op(OPERATOR_RPAREN))
        {
-         this->list(pfn, varg, true);
+         this->list(pfn, true);
          if (!this->peek_token()->is_op(OPERATOR_RPAREN))
            {
              go_error_at(this->location(), "missing %<)%>");
@@ -1383,10 +1410,10 @@ Parse::decl(void (Parse::*pfn)(void*, unsigned int), 
void* varg,
 // might follow.  This is either a '}' or a ')'.
 
 void
-Parse::list(void (Parse::*pfn)(void*, unsigned int), void* varg,
+Parse::list(void (Parse::*pfn)(unsigned int, std::vector<std::string>*),
            bool follow_is_paren)
 {
-  (this->*pfn)(varg, 0);
+  (this->*pfn)(0, NULL);
   Operator follow = follow_is_paren ? OPERATOR_RPAREN : OPERATOR_RCURLY;
   while (this->peek_token()->is_op(OPERATOR_SEMICOLON)
         || this->peek_token()->is_op(OPERATOR_COMMA))
@@ -1395,7 +1422,7 @@ Parse::list(void (Parse::*pfn)(void*, unsigned int), 
void* varg,
        go_error_at(this->location(), "unexpected comma");
       if (this->advance_token()->is_op(follow))
        break;
-      (this->*pfn)(varg, 0);
+      (this->*pfn)(0, NULL);
     }
 }
 
@@ -1522,13 +1549,13 @@ Parse::type_decl(unsigned int pragmas)
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_TYPE));
   this->advance_token();
-  this->decl(&Parse::type_spec, NULL, pragmas);
+  this->decl(&Parse::type_spec, pragmas, NULL);
 }
 
 // TypeSpec = identifier ["="] Type .
 
 void
-Parse::type_spec(void*, unsigned int pragmas)
+Parse::type_spec(unsigned int pragmas, std::vector<std::string>*)
 {
   const Token* token = this->peek_token();
   if (!token->is_identifier())
@@ -1622,27 +1649,42 @@ Parse::type_spec(void*, unsigned int pragmas)
 // VarDecl = "var" Decl<VarSpec> .
 
 void
-Parse::var_decl()
+Parse::var_decl(std::vector<std::string>* embeds)
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_VAR));
   this->advance_token();
-  this->decl(&Parse::var_spec, NULL, 0);
+  this->decl(&Parse::var_spec, 0, embeds);
 }
 
 // VarSpec = IdentifierList
 //             ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) .
 
 void
-Parse::var_spec(void*, unsigned int pragmas)
+Parse::var_spec(unsigned int pragmas, std::vector<std::string>* embeds)
 {
+  Location loc = this->location();
+
   if (pragmas != 0)
-    go_warning_at(this->location(), 0,
-                 "ignoring magic %<//go:...%> comment before var");
+    go_warning_at(loc, 0, "ignoring magic %<//go:...%> comment before var");
 
   // Get the variable names.
   Typed_identifier_list til;
   this->identifier_list(&til);
 
+  if (embeds != NULL)
+    {
+      if (!this->gogo_->in_global_scope())
+       {
+         go_error_at(loc, "go:embed only permitted at package scope");
+         embeds = NULL;
+       }
+      if (til.size() > 1)
+       {
+         go_error_at(loc, "go:embed cannot apply to multiple vars");
+         embeds = NULL;
+       }
+    }
+
   Location location = this->location();
 
   Type* type = NULL;
@@ -1670,7 +1712,13 @@ Parse::var_spec(void*, unsigned int pragmas)
       init = this->expression_list(NULL, false, true);
     }
 
-  this->init_vars(&til, type, init, false, location);
+  if (embeds != NULL && init != NULL)
+    {
+      go_error_at(loc, "go:embed cannot apply to var with initializer");
+      embeds = NULL;
+    }
+
+  this->init_vars(&til, type, init, false, embeds, location);
 
   if (init != NULL)
     delete init;
@@ -1683,11 +1731,12 @@ Parse::var_spec(void*, unsigned int pragmas)
 void
 Parse::init_vars(const Typed_identifier_list* til, Type* type,
                 Expression_list* init, bool is_coloneq,
-                Location location)
+                std::vector<std::string>* embeds, Location location)
 {
   // Check for an initialization which can yield multiple values.
   if (init != NULL && init->size() == 1 && til->size() > 1)
     {
+      go_assert(embeds == NULL);
       if (this->init_vars_from_call(til, type, *init->begin(), is_coloneq,
                                    location))
        return;
@@ -1729,8 +1778,12 @@ Parse::init_vars(const Typed_identifier_list* til, Type* 
type,
     {
       if (init != NULL)
        go_assert(pexpr != init->end());
-      this->init_var(*p, type, init == NULL ? NULL : *pexpr, is_coloneq,
-                    false, &any_new, vars, vals);
+      Named_object* no = this->init_var(*p, type,
+                                       init == NULL ? NULL : *pexpr,
+                                       is_coloneq, false, &any_new,
+                                       vars, vals);
+      if (embeds != NULL && no->is_variable())
+       no->var_value()->set_embeds(embeds);
       if (init != NULL)
        ++pexpr;
     }
@@ -2270,7 +2323,7 @@ Parse::simple_var_decl_or_assignment(const std::string& 
name,
        }
     }
 
-  this->init_vars(&til, NULL, init, true, location);
+  this->init_vars(&til, NULL, init, true, NULL, location);
 }
 
 // FunctionDecl = "func" identifier Signature [ Block ] .
@@ -5317,7 +5370,7 @@ Parse::for_stat(Label* label)
        {
          go_error_at(this->location(),
                       "var declaration not allowed in for initializer");
-         this->var_decl();
+         this->var_decl(NULL);
        }
 
       if (token->is_op(OPERATOR_SEMICOLON))
@@ -5762,13 +5815,13 @@ Parse::import_decl()
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_IMPORT));
   this->advance_token();
-  this->decl(&Parse::import_spec, NULL, 0);
+  this->decl(&Parse::import_spec, 0, NULL);
 }
 
 // ImportSpec = [ "." | PackageName ] PackageFileName .
 
 void
-Parse::import_spec(void*, unsigned int pragmas)
+Parse::import_spec(unsigned int pragmas, std::vector<std::string>*)
 {
   if (pragmas != 0)
     go_warning_at(this->location(), 0,
diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h
index 4a5a4b8fbe4..2c3c505ffa9 100644
--- a/gcc/go/gofrontend/parse.h
+++ b/gcc/go/gofrontend/parse.h
@@ -181,16 +181,17 @@ class Parse
   void method_spec(Typed_identifier_list*);
   void declaration();
   bool declaration_may_start_here();
-  void decl(void (Parse::*)(void*, unsigned int), void*, unsigned int pragmas);
-  void list(void (Parse::*)(void*, unsigned int), void*, bool);
+  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 const_decl();
   void const_spec(int, Type**, Expression_list**);
   void type_decl(unsigned int pragmas);
-  void type_spec(void*, unsigned int pragmas);
-  void var_decl();
-  void var_spec(void*, 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 init_vars(const Typed_identifier_list*, Type*, Expression_list*,
-                bool is_coloneq, Location);
+                bool is_coloneq, std::vector<std::string>*, Location);
   bool init_vars_from_call(const Typed_identifier_list*, Type*, Expression*,
                           bool is_coloneq, Location);
   bool init_vars_from_map(const Typed_identifier_list*, Type*, Expression*,
@@ -277,7 +278,7 @@ class Parse
   void goto_stat();
   void package_clause();
   void import_decl();
-  void import_spec(void*, unsigned int pragmas);
+  void import_spec(unsigned int pragmas, std::vector<std::string>*);
 
   // Skip past an error looking for a semicolon or OP.  Return true if
   // all is well, false if we found EOF.

Reply via email to