diff mbox

Go patch committed: Implement //go:nointerface

Message ID mcrpq2ypp1k.fsf@google.com
State New
Headers show

Commit Message

Ian Lance Taylor Nov. 28, 2012, 12:08 a.m. UTC
As part of a code analysis experiment, the gc compiler implements a
feature based on comments: if a //go:nointerface comment appears before
a method for a type, that method is not included in any interfaces.
This works in conjunction with a field tracking feature, not yet
implemented in gccgo, that lets a program determine which struct fields
are actually referenced.  This patch implements //go:nointerface for
gccgo.  Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline.

Ian
diff mbox

Patch

diff -r 2fe578353c73 go/gogo.cc
--- a/go/gogo.cc	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/gogo.cc	Tue Nov 27 16:05:11 2012 -0800
@@ -3074,8 +3074,8 @@ 
   : 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 -r 2fe578353c73 go/gogo.h
--- a/go/gogo.h	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/gogo.h	Tue Nov 27 16:05:11 2012 -0800
@@ -911,6 +911,24 @@ 
   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 @@ 
   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 -r 2fe578353c73 go/lex.cc
--- a/go/lex.cc	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/lex.cc	Tue Nov 27 16:05:11 2012 -0800
@@ -442,7 +442,8 @@ 
 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 @@ 
 	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 -r 2fe578353c73 go/lex.h
--- a/go/lex.h	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/lex.h	Tue Nov 27 16:05:11 2012 -0800
@@ -349,6 +349,16 @@ 
   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 @@ 
   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 -r 2fe578353c73 go/parse.cc
--- a/go/parse.cc	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/parse.cc	Tue Nov 27 16:05:11 2012 -0800
@@ -1280,6 +1280,12 @@ 
 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 @@ 
   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 @@ 
 // 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 @@ 
       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 @@ 
 		}
 	    }
 	}
+
+      if (saw_nointerface)
+	warning_at(location, 0,
+		   ("ignoring magic //go:nointerface comment "
+		    "before declaration"));
     }
   else
     {
@@ -2268,9 +2288,13 @@ 
 	    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 -r 2fe578353c73 go/parse.h
--- a/go/parse.h	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/parse.h	Tue Nov 27 16:05:11 2012 -0800
@@ -214,7 +214,7 @@ 
   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 -r 2fe578353c73 go/types.cc
--- a/go/types.cc	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/types.cc	Tue Nov 27 16:05:11 2012 -0800
@@ -2068,6 +2068,13 @@ 
 	    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 @@ 
 	    }
 	  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 @@ 
   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_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 -r 2fe578353c73 go/types.h
--- a/go/types.h	Sat Nov 24 16:51:19 2012 -0800
+++ b/go/types.h	Tue Nov 27 16:05:11 2012 -0800
@@ -179,6 +179,12 @@ 
     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 @@ 
   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 @@ 
   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 @@ 
   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_;