compiler: Implement //go:nointerface comments.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 28 Nov 2012 00:08:16 +0000 (00:08 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 28 Nov 2012 00:08:16 +0000 (00:08 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@193874 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/lex.cc
gcc/go/gofrontend/lex.h
gcc/go/gofrontend/parse.cc
gcc/go/gofrontend/parse.h
gcc/go/gofrontend/types.cc
gcc/go/gofrontend/types.h

index 5b56610..766fe90 100644 (file)
@@ -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)
 {
 }
 
index 8759d32..e23b3c6 100644 (file)
@@ -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.
index 6add84e..804146d 100644 (file)
@@ -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_;
index 074bbae..fc9258b 100644 (file)
@@ -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_;
index 72059b8..fe463c7 100644 (file)
@@ -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;
     }
 }
index a355b7d..9743bb5 100644 (file)
@@ -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*,
index 1990b03..7a4345d 100644 (file)
@@ -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);
index a62a6cf..2ab21f6 100644 (file)
@@ -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_;