diff options
author | Ian Lance Taylor <iant@google.com> | 2012-11-28 00:08:16 +0000 |
---|---|---|
committer | Ian Lance Taylor <iant@google.com> | 2012-11-28 00:08:16 +0000 |
commit | 2a2a9b991cd4b709e2f8198ff59d7e649b694f27 (patch) | |
tree | 35fac20e209e18c93ef872452387ae58623599d0 /gcc/go | |
parent | 4460b49831aa8dcdb93753aae83b50207988edd4 (diff) |
compiler: Implement //go:nointerface comments.
git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@193874 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 4 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 20 | ||||
-rw-r--r-- | gcc/go/gofrontend/lex.cc | 9 | ||||
-rw-r--r-- | gcc/go/gofrontend/lex.h | 12 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 30 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.h | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 37 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 19 |
8 files changed, 126 insertions, 7 deletions
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 5b566103ec8..766fe908095 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -3074,8 +3074,8 @@ Function::Function(Function_type* type, Function* enclosing, Block* block, : type_(type), enclosing_(enclosing), results_(NULL), closure_var_(NULL), block_(block), location_(location), labels_(), local_type_count_(0), fndecl_(NULL), defer_stack_(NULL), - results_are_named_(false), calls_recover_(false), is_recover_thunk_(false), - has_recover_thunk_(false) + results_are_named_(false), nointerface_(false), calls_recover_(false), + is_recover_thunk_(false), has_recover_thunk_(false) { } diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 8759d32b6c9..e23b3c65072 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -911,6 +911,24 @@ class Function results_are_named() const { return this->results_are_named_; } + // Whether this method should not be included in the type + // descriptor. + bool + nointerface() const + { + go_assert(this->is_method()); + return this->nointerface_; + } + + // Record that this method should not be included in the type + // descriptor. + void + set_nointerface() + { + go_assert(this->is_method()); + this->nointerface_ = true; + } + // Add a new field to the closure variable. void add_closure_field(Named_object* var, Location loc) @@ -1113,6 +1131,8 @@ class Function Temporary_statement* defer_stack_; // True if the result variables are named. bool results_are_named_; + // True if this method should not be included in the type descriptor. + bool nointerface_; // True if this function calls the predeclared recover function. bool calls_recover_; // True if this a thunk built for a function which calls recover. diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc index 6add84ed1f7..804146d0e9f 100644 --- a/gcc/go/gofrontend/lex.cc +++ b/gcc/go/gofrontend/lex.cc @@ -442,7 +442,8 @@ Token::print(FILE* file) const Lex::Lex(const char* input_file_name, FILE* input_file, Linemap* linemap) : input_file_name_(input_file_name), input_file_(input_file), linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0), - lineoff_(0), lineno_(0), add_semi_at_eol_(false), extern_() + lineoff_(0), lineno_(0), add_semi_at_eol_(false), saw_nointerface_(false), + extern_() { this->linebuf_ = new char[this->linebufsize_]; this->linemap_->start_file(input_file_name, 0); @@ -1704,6 +1705,12 @@ Lex::skip_cpp_comment() this->extern_ = std::string(p, plend - p); } + // For field tracking analysis: a //go:nointerface comment means + // that the next interface method should not be stored in the type + // descriptor. This permits it to be discarded if it is not needed. + if (this->lineoff_ == 2 && memcmp(p, "go:nointerface", 14) == 0) + this->saw_nointerface_ = true; + while (p < pend) { this->lineoff_ = p - this->linebuf_; diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h index 074bbaea4ed..fc9258b880a 100644 --- a/gcc/go/gofrontend/lex.h +++ b/gcc/go/gofrontend/lex.h @@ -349,6 +349,16 @@ class Lex extern_name() const { return this->extern_; } + // Return whether we have seen a //go:nointerface comment, clearing + // the flag. + bool + get_and_clear_nointerface() + { + bool ret = this->saw_nointerface_; + this->saw_nointerface_ = false; + return ret; + } + // Return whether the identifier NAME should be exported. NAME is a // mangled name which includes only ASCII characters. static bool @@ -483,6 +493,8 @@ class Lex size_t lineno_; // Whether to add a semicolon if we see a newline now. bool add_semi_at_eol_; + // Whether we just saw a magic go:nointerface comment. + bool saw_nointerface_; // The external name to use for a function declaration, from a magic // //extern comment. std::string extern_; diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 72059b88a55..fe463c7d66f 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -1280,6 +1280,12 @@ void Parse::declaration() { const Token* token = this->peek_token(); + + bool saw_nointerface = this->lex_->get_and_clear_nointerface(); + if (saw_nointerface && !token->is_keyword(KEYWORD_FUNC)) + warning_at(token->location(), 0, + "ignoring magic //go:nointerface comment before non-method"); + if (token->is_keyword(KEYWORD_CONST)) this->const_decl(); else if (token->is_keyword(KEYWORD_TYPE)) @@ -1287,7 +1293,7 @@ Parse::declaration() else if (token->is_keyword(KEYWORD_VAR)) this->var_decl(); else if (token->is_keyword(KEYWORD_FUNC)) - this->function_decl(); + this->function_decl(saw_nointerface); else { error_at(this->location(), "expected declaration"); @@ -2166,8 +2172,11 @@ Parse::simple_var_decl_or_assignment(const std::string& name, // inside the asm. This extension will be removed at some future // date. It has been replaced with //extern comments. +// SAW_NOINTERFACE is true if we saw a magic //go:nointerface comment, +// which means that we omit the method from the type descriptor. + void -Parse::function_decl() +Parse::function_decl(bool saw_nointerface) { go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC)); Location location = this->location(); @@ -2180,6 +2189,12 @@ Parse::function_decl() rec = this->receiver(); token = this->peek_token(); } + else if (saw_nointerface) + { + warning_at(location, 0, + "ignoring magic //go:nointerface comment before non-method"); + saw_nointerface = false; + } if (!token->is_identifier()) { @@ -2256,6 +2271,11 @@ Parse::function_decl() } } } + + if (saw_nointerface) + warning_at(location, 0, + ("ignoring magic //go:nointerface comment " + "before declaration")); } else { @@ -2268,9 +2288,13 @@ Parse::function_decl() this->gogo_->add_erroneous_name(name); name = this->gogo_->pack_hidden_name("_", false); } - this->gogo_->start_function(name, fntype, true, location); + named_object = this->gogo_->start_function(name, fntype, true, location); Location end_loc = this->block(); this->gogo_->finish_function(end_loc); + if (saw_nointerface + && !this->is_erroneous_function_ + && named_object->is_function()) + named_object->func_value()->set_nointerface(); this->is_erroneous_function_ = hold_is_erroneous_function; } } diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h index a355b7d2b3b..9743bb52ebf 100644 --- a/gcc/go/gofrontend/parse.h +++ b/gcc/go/gofrontend/parse.h @@ -214,7 +214,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(); + void function_decl(bool saw_nointerface); Typed_identifier* receiver(); Expression* operand(bool may_be_sink); Expression* enclosing_var_reference(Named_object*, Named_object*, diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 1990b03c3f8..7a4345dfa2c 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -2068,6 +2068,13 @@ Type::methods_constructor(Gogo* gogo, Type* methods_type, continue; if (only_value_methods && !p->second->is_value_method()) continue; + + // This is where we implement the magic //go:nointerface + // comment. If we saw that comment, we don't add this + // method to the type descriptor. + if (p->second->nointerface()) + continue; + smethods.push_back(std::make_pair(p->first, p->second)); } } @@ -6891,6 +6898,24 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const } return false; } + + // If the magic //go:nointerface comment was used, the method + // may not be used to implement interfaces. + if (m->nointerface()) + { + if (reason != NULL) + { + std::string n = Gogo::message_name(p->name()); + size_t len = 100 + n.length(); + char* buf = new char[len]; + snprintf(buf, len, + _("method %s%s%s is marked go:nointerface"), + open_quote, n.c_str(), close_quote); + reason->assign(buf); + delete[] buf; + } + return false; + } } return true; @@ -7530,6 +7555,15 @@ Named_method::do_bind_method(Expression* expr, Location location) const return bme; } +// Return whether this method should not participate in interfaces. + +bool +Named_method::do_nointerface() const +{ + Named_object* no = this->named_object_; + return no->is_function() && no->func_value()->nointerface(); +} + // Class Interface_method. // Bind a method to an object. @@ -8834,6 +8868,9 @@ Type::build_stub_methods(Gogo* gogo, const Type* type, const Methods* methods, Type::build_one_stub_method(gogo, m, buf, stub_params, fntype->is_varargs(), location); gogo->finish_function(fntype->location()); + + if (m->nointerface() && stub->is_function()) + stub->func_value()->set_nointerface(); } m->set_stub_object(stub); diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index a62a6cfbf97..2ab21f65355 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -179,6 +179,12 @@ class Method this->stub_ = no; } + // Return true if this method should not participate in any + // interfaces. + bool + nointerface() const + { return this->do_nointerface(); } + protected: // These objects are only built by the child classes. Method(const Field_indexes* field_indexes, unsigned int depth, @@ -204,6 +210,10 @@ class Method virtual Expression* do_bind_method(Expression* expr, Location location) const = 0; + // Return whether this method should not participate in interfaces. + virtual bool + do_nointerface() const = 0; + private: // The sequence of field indexes used for this method. If this is // NULL, then the method is defined for the current type. @@ -254,6 +264,10 @@ class Named_method : public Method Expression* do_bind_method(Expression* expr, Location location) const; + // Return whether this method should not participate in interfaces. + bool + do_nointerface() const; + private: // The method itself. For a method which needs a stub, this starts // out as the underlying method, and is later replaced with the stub @@ -295,6 +309,11 @@ class Interface_method : public Method Expression* do_bind_method(Expression* expr, Location location) const; + // Return whether this method should not participate in interfaces. + bool + do_nointerface() const + { return false; } + private: // The name of the interface method to call. std::string name_; |