@@ -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)
{
}
@@ -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.
@@ -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_;
@@ -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_;
@@ -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;
}
}
@@ -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*,
@@ -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);
@@ -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_;