From c907e4394133b3f4b58feccfb3a415f9ae5ee1f4 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 4 Jan 2021 21:40:29 -0800 Subject: [PATCH] compiler: read embedcfg files, parse go:embed directives This change reads go:embed directives and attaches them to variables. We still don't do anything with the directives. This change also reads the file passed in the -fgo-embedcfg option. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/281533 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/embed.cc | 15 ++++++ gcc/go/gofrontend/go.cc | 2 + gcc/go/gofrontend/gogo.cc | 4 +- gcc/go/gofrontend/gogo.h | 16 ++++++ gcc/go/gofrontend/lex.cc | 94 ++++++++++++++++++++++++++++++++++++ gcc/go/gofrontend/lex.h | 20 ++++++++ gcc/go/gofrontend/parse.cc | 99 +++++++++++++++++++++++++++++--------- gcc/go/gofrontend/parse.h | 15 +++--- 9 files changed, 234 insertions(+), 33 deletions(-) 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* 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* 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* 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 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* embeds = NULL; + if (this->lex_->has_embeds()) + { + embeds = new(std::vector); + this->lex_->get_and_clear_embeds(embeds); + + if (!this->gogo_->is_embed_imported()) + { + go_error_at(token->location(), + "invalid go:embed: missing import %"); + 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 | "(" [ List

] ")" . void -Parse::decl(void (Parse::*pfn)(void*, unsigned int), void* varg, - unsigned int pragmas) +Parse::decl(void (Parse::*pfn)(unsigned int, std::vector*), + unsigned int pragmas, std::vector* 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 % comment before group"); + if (embeds != NULL) + go_error_at(this->location(), + "ignoring % 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*), 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*) { const Token* token = this->peek_token(); if (!token->is_identifier()) @@ -1622,27 +1649,42 @@ Parse::type_spec(void*, unsigned int pragmas) // VarDecl = "var" Decl . void -Parse::var_decl() +Parse::var_decl(std::vector* 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* embeds) { + Location loc = this->location(); + if (pragmas != 0) - go_warning_at(this->location(), 0, - "ignoring magic % comment before var"); + go_warning_at(loc, 0, "ignoring magic % 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* 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*) { 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*), + unsigned int pragmas, std::vector* embeds); + void list(void (Parse::*)(unsigned int, std::vector*), 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*); + void var_decl(std::vector* embeds); + void var_spec(unsigned int pragmas, std::vector*); void init_vars(const Typed_identifier_list*, Type*, Expression_list*, - bool is_coloneq, Location); + bool is_coloneq, std::vector*, 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*); // Skip past an error looking for a semicolon or OP. Return true if // all is well, false if we found EOF. -- 2.30.2