Commit b87060ce by Ian Lance Taylor

compiler: rewrite compiler directive support

    
    Rewrite the compiler directive support to recognize all the compiler
    directives implemented by the current gc compiler.  The directives other
    than go:linkname are now turned into GOPRAGMA flags attached to a
    function or function declaration.  The go:linkname directive is turned
    into a map attached to the Lex object.  No new directives are actually
    implemented yet, they are just recognized.
    
    Reviewed-on: https://go-review.googlesource.com/26610

From-SVN: r239282
parent a00f4c6b
3b9c57a35370f26e6cf5839084e367e75e45ec97 58be5c6c7d92182dec50a62c8e319d2d7aab12a4
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -4439,7 +4439,7 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block, ...@@ -4439,7 +4439,7 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block,
: type_(type), enclosing_(enclosing), results_(NULL), : type_(type), enclosing_(enclosing), results_(NULL),
closure_var_(NULL), block_(block), location_(location), labels_(), closure_var_(NULL), block_(block), location_(location), labels_(),
local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL), local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL),
is_sink_(false), results_are_named_(false), nointerface_(false), pragmas_(0), is_sink_(false), results_are_named_(false),
is_unnamed_type_stub_method_(false), calls_recover_(false), is_unnamed_type_stub_method_(false), calls_recover_(false),
is_recover_thunk_(false), has_recover_thunk_(false), is_recover_thunk_(false), has_recover_thunk_(false),
calls_defer_retaddr_(false), is_type_specific_function_(false), calls_defer_retaddr_(false), is_type_specific_function_(false),
...@@ -4511,6 +4511,24 @@ Function::update_result_variables() ...@@ -4511,6 +4511,24 @@ Function::update_result_variables()
(*p)->result_var_value()->set_function(this); (*p)->result_var_value()->set_function(this);
} }
// Whether this method should not be included in the type descriptor.
bool
Function::nointerface() const
{
go_assert(this->is_method());
return (this->pragmas_ & GOPRAGMA_NOINTERFACE) != 0;
}
// Record that this method should not be included in the type
// descriptor.
void
Function::set_nointerface()
{
this->pragmas_ |= GOPRAGMA_NOINTERFACE;
}
// Return the closure variable, creating it if necessary. // Return the closure variable, creating it if necessary.
Named_object* Named_object*
...@@ -5042,7 +5060,8 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) ...@@ -5042,7 +5060,8 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
// We want to put a nointerface function into a unique section // We want to put a nointerface function into a unique section
// because there is a good chance that the linker garbage // because there is a good chance that the linker garbage
// collection can discard it. // collection can discard it.
bool in_unique_section = this->in_unique_section_ || this->nointerface_; bool in_unique_section = (this->in_unique_section_
|| (this->is_method() && this->nointerface()));
Btype* functype = this->type_->get_backend_fntype(gogo); Btype* functype = this->type_->get_backend_fntype(gogo);
this->fndecl_ = this->fndecl_ =
......
...@@ -975,23 +975,22 @@ class Function ...@@ -975,23 +975,22 @@ class Function
results_are_named() const results_are_named() const
{ return this->results_are_named_; } { return this->results_are_named_; }
// Set the pragmas for this function.
void
set_pragmas(unsigned int pragmas)
{
this->pragmas_ = pragmas;
}
// Whether this method should not be included in the type // Whether this method should not be included in the type
// descriptor. // descriptor.
bool bool
nointerface() const nointerface() const;
{
go_assert(this->is_method());
return this->nointerface_;
}
// Record that this method should not be included in the type // Record that this method should not be included in the type
// descriptor. // descriptor.
void void
set_nointerface() set_nointerface();
{
go_assert(this->is_method());
this->nointerface_ = true;
}
// Record that this function is a stub method created for an unnamed // Record that this function is a stub method created for an unnamed
// type. // type.
...@@ -1238,12 +1237,12 @@ class Function ...@@ -1238,12 +1237,12 @@ class Function
// distinguish the defer stack for one function from another. This // distinguish the defer stack for one function from another. This
// is NULL unless we actually need a defer stack. // is NULL unless we actually need a defer stack.
Temporary_statement* defer_stack_; Temporary_statement* defer_stack_;
// Pragmas for this function. This is a set of GOPRAGMA bits.
unsigned int pragmas_;
// True if this function is sink-named. No code is generated. // True if this function is sink-named. No code is generated.
bool is_sink_ : 1; bool is_sink_ : 1;
// True if the result variables are named. // True if the result variables are named.
bool results_are_named_ : 1; bool results_are_named_ : 1;
// True if this method should not be included in the type descriptor.
bool nointerface_ : 1;
// True if this function is a stub method created for an unnamed // True if this function is a stub method created for an unnamed
// type. // type.
bool is_unnamed_type_stub_method_ : 1; bool is_unnamed_type_stub_method_ : 1;
...@@ -1305,7 +1304,7 @@ class Function_declaration ...@@ -1305,7 +1304,7 @@ class Function_declaration
public: public:
Function_declaration(Function_type* fntype, Location location) Function_declaration(Function_type* fntype, Location location)
: fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL), : fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL),
fndecl_(NULL) fndecl_(NULL), pragmas_(0)
{ } { }
Function_type* Function_type*
...@@ -1325,6 +1324,13 @@ class Function_declaration ...@@ -1325,6 +1324,13 @@ class Function_declaration
set_asm_name(const std::string& asm_name) set_asm_name(const std::string& asm_name)
{ this->asm_name_ = asm_name; } { this->asm_name_ = asm_name; }
// Set the pragmas for this function.
void
set_pragmas(unsigned int pragmas)
{
this->pragmas_ = pragmas;
}
// Return an expression for the function descriptor, given the named // Return an expression for the function descriptor, given the named
// object for this function. This may only be called for functions // object for this function. This may only be called for functions
// without a closure. This will be an immutable struct with one // without a closure. This will be an immutable struct with one
...@@ -1367,6 +1373,8 @@ class Function_declaration ...@@ -1367,6 +1373,8 @@ class Function_declaration
Expression* descriptor_; Expression* descriptor_;
// The function decl if needed. // The function decl if needed.
Bfunction* fndecl_; Bfunction* fndecl_;
// Pragmas for this function. This is a set of GOPRAGMA bits.
unsigned int pragmas_;
}; };
// A variable. // A variable.
......
...@@ -442,7 +442,7 @@ Token::print(FILE* file) const ...@@ -442,7 +442,7 @@ Token::print(FILE* file) const
Lex::Lex(const char* input_file_name, FILE* input_file, Linemap* linemap) Lex::Lex(const char* input_file_name, FILE* input_file, Linemap* linemap)
: input_file_name_(input_file_name), input_file_(input_file), : input_file_name_(input_file_name), input_file_(input_file),
linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0), linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0),
lineoff_(0), lineno_(0), add_semi_at_eol_(false), saw_nointerface_(false), lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0),
extern_() extern_()
{ {
this->linebuf_ = new char[this->linebufsize_]; this->linebuf_ = new char[this->linebufsize_];
...@@ -1676,29 +1676,47 @@ Lex::skip_cpp_comment() ...@@ -1676,29 +1676,47 @@ Lex::skip_cpp_comment()
// //extern comment. // //extern comment.
this->extern_.clear(); this->extern_.clear();
const char* p = this->linebuf_ + this->lineoff_; size_t lineoff = this->lineoff_;
const char* p = this->linebuf_ + lineoff;
const char* pend = this->linebuf_ + this->linesize_; const char* pend = this->linebuf_ + this->linesize_;
// By convention, a C++ comment at the start of the line of the form const char* pcheck = p;
bool saw_error = false;
while (pcheck < pend)
{
this->lineoff_ = pcheck - this->linebuf_;
unsigned int c;
bool issued_error;
pcheck = this->advance_one_utf8_char(pcheck, &c, &issued_error);
if (issued_error)
saw_error = true;
}
if (saw_error)
return;
// Recognize various magic comments at the start of a line.
if (lineoff != 2)
{
// Not at the start of the line. (lineoff == 2 because of the
// two characters in "//").
return;
}
while (pend > p
&& (pend[-1] == ' ' || pend[-1] == '\t'
|| pend[-1] == '\r' || pend[-1] == '\n'))
--pend;
// A C++ comment at the start of the line of the form
// //line FILE:LINENO // //line FILE:LINENO
// is interpreted as setting the file name and line number of the // is interpreted as setting the file name and line number of the
// next source line. // next source line.
if (pend - p > 5 && memcmp(p, "line ", 5) == 0)
if (this->lineoff_ == 2
&& pend - p > 5
&& memcmp(p, "line ", 5) == 0)
{ {
p += 5; p += 5;
// Before finding FILE:LINENO, make sure line has valid characters.
const char* pcheck = p;
while (pcheck < pend)
{
unsigned int c;
bool issued_error;
pcheck = this->advance_one_utf8_char(pcheck, &c, &issued_error);
}
while (p < pend && *p == ' ') while (p < pend && *p == ' ')
++p; ++p;
const char* pcolon = static_cast<const char*>(memchr(p, ':', pend - p)); const char* pcolon = static_cast<const char*>(memchr(p, ':', pend - p));
...@@ -1726,6 +1744,7 @@ Lex::skip_cpp_comment() ...@@ -1726,6 +1744,7 @@ Lex::skip_cpp_comment()
p = plend; p = plend;
} }
} }
return;
} }
// As a special gccgo extension, a C++ comment at the start of the // As a special gccgo extension, a C++ comment at the start of the
...@@ -1734,37 +1753,129 @@ Lex::skip_cpp_comment() ...@@ -1734,37 +1753,129 @@ Lex::skip_cpp_comment()
// which immediately precedes a function declaration means that the // which immediately precedes a function declaration means that the
// external name of the function declaration is NAME. This is // external name of the function declaration is NAME. This is
// normally used to permit Go code to call a C function. // normally used to permit Go code to call a C function.
if (this->lineoff_ == 2 if (pend - p > 7 && memcmp(p, "extern ", 7) == 0)
&& pend - p > 7
&& memcmp(p, "extern ", 7) == 0)
{ {
p += 7; p += 7;
while (p < pend && (*p == ' ' || *p == '\t')) while (p < pend && (*p == ' ' || *p == '\t'))
++p; ++p;
const char* plend = pend; if (pend > p)
while (plend > p this->extern_ = std::string(p, pend - p);
&& (plend[-1] == ' ' || plend[-1] == '\t' || plend[-1] == '\n')) return;
--plend;
if (plend > p)
this->extern_ = std::string(p, plend - p);
} }
// For field tracking analysis: a //go:nointerface comment means // All other special comments start with "go:".
// 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
&& pend - p > 14
&& memcmp(p, "go:nointerface", 14) == 0)
this->saw_nointerface_ = true;
while (p < pend) if (pend - p < 4 || memcmp(p, "go:", 3) != 0)
return;
const char *ps = p + 3;
while (ps < pend && *ps != ' ' && *ps != '\t')
++ps;
std::string verb = std::string(p, ps - p);
if (verb == "go:linkname")
{ {
this->lineoff_ = p - this->linebuf_; // As in the gc compiler, set the external link name for a Go symbol.
unsigned int c; std::string go_name;
bool issued_error; std::string c_name;
p = this->advance_one_utf8_char(p, &c, &issued_error); if (ps < pend)
if (issued_error) {
this->extern_.clear(); while (ps < pend && (*ps == ' ' || *ps == '\t'))
++ps;
if (ps < pend)
{
const char* pg = ps;
while (ps < pend && *ps != ' ' && *ps != '\t')
++ps;
if (ps < pend)
go_name = std::string(pg, ps - pg);
while (ps < pend && (*ps == ' ' || *ps == '\t'))
++ps;
}
if (ps < pend)
{
const char* pc = ps;
while (ps < pend && *ps != ' ' && *ps != '\t')
++ps;
if (ps <= pend)
c_name = std::string(pc, ps - pc);
}
if (ps != pend)
{
go_name.clear();
c_name.clear();
}
}
if (go_name.empty() || c_name.empty())
error_at(this->location(), "usage: //go:linkname localname linkname");
else
this->linknames_[go_name] = c_name;
}
else if (verb == "go:nointerface")
{
// 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.
this->pragmas_ |= GOPRAGMA_NOINTERFACE;
}
else if (verb == "go:noescape")
{
// Applies to the next function declaration. Any arguments do
// not escape.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_NOESCAPE;
}
else if (verb == "go:nosplit")
{
// Applies to the next function. Do not split the stack when
// entering the function.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_NOSPLIT;
}
else if (verb == "go:noinline")
{
// Applies to the next function. Do not inline the function.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_NOINLINE;
}
else if (verb == "go:systemstack")
{
// Applies to the next function. It must run on the system stack.
// FIXME: Should only work when compiling the runtime package.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_SYSTEMSTACK;
}
else if (verb == "go:nowritebarrier")
{
// Applies to the next function. If the function needs to use
// any write barriers, it should emit an error instead.
// FIXME: Should only work when compiling the runtime package.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_NOWRITEBARRIER;
}
else if (verb == "go:nowritebarrierrec")
{
// Applies to the next function. If the function, or any
// function that it calls, needs to use any write barriers, it
// should emit an error instead.
// FIXME: Should only work when compiling the runtime package.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_NOWRITEBARRIERREC;
}
else if (verb == "go:cgo_unsafe_args")
{
// Applies to the next function. Taking the address of any
// argument implies taking the address of all arguments.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_CGOUNSAFEARGS;
}
else if (verb == "go:uintptrescapes")
{
// Applies to the next function. If an argument is a pointer
// converted to uintptr, then the pointer escapes.
// FIXME: Not implemented.
this->pragmas_ |= GOPRAGMA_UINTPTRESCAPES;
} }
} }
......
...@@ -49,6 +49,24 @@ enum Keyword ...@@ -49,6 +49,24 @@ enum Keyword
KEYWORD_VAR KEYWORD_VAR
}; };
// Pragmas built from magic comments and recorded for functions.
// These are used as bits in a bitmask.
// The set of values is intended to be the same as the gc compiler.
enum GoPragma
{
GOPRAGMA_NOINTERFACE = 1 << 0, // Method not in type descriptor.
GOPRAGMA_NOESCAPE = 1 << 1, // Args do not escape.
GOPRAGMA_NORACE = 1 << 2, // No race detector.
GOPRAGMA_NOSPLIT = 1 << 3, // Do not split stack.
GOPRAGMA_NOINLINE = 1 << 4, // Do not inline.
GOPRAGMA_SYSTEMSTACK = 1 << 5, // Must run on system stack.
GOPRAGMA_NOWRITEBARRIER = 1 << 6, // No write barriers.
GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees.
GOPRAGMA_CGOUNSAFEARGS = 1 << 8, // Pointer to arg is pointer to all.
GOPRAGMA_UINTPTRESCAPES = 1 << 9 // uintptr(p) escapes.
};
// A token returned from the lexer. // A token returned from the lexer.
class Token class Token
...@@ -348,13 +366,12 @@ class Lex ...@@ -348,13 +366,12 @@ class Lex
extern_name() const extern_name() const
{ return this->extern_; } { return this->extern_; }
// Return whether we have seen a //go:nointerface comment, clearing // Return the current set of pragmas, and clear them.
// the flag. unsigned int
bool get_and_clear_pragmas()
get_and_clear_nointerface()
{ {
bool ret = this->saw_nointerface_; unsigned int ret = this->pragmas_;
this->saw_nointerface_ = false; this->pragmas_ = 0;
return ret; return ret;
} }
...@@ -492,11 +509,13 @@ class Lex ...@@ -492,11 +509,13 @@ class Lex
size_t lineno_; size_t lineno_;
// Whether to add a semicolon if we see a newline now. // Whether to add a semicolon if we see a newline now.
bool add_semi_at_eol_; bool add_semi_at_eol_;
// Whether we just saw a magic go:nointerface comment. // Pragmas for the next function, from magic comments.
bool saw_nointerface_; unsigned int pragmas_;
// The external name to use for a function declaration, from a magic // The external name to use for a function declaration, from a magic
// //extern comment. // //extern comment.
std::string extern_; std::string extern_;
// The list of //go:linkname comments.
std::map<std::string, std::string> linknames_;
}; };
#endif // !defined(GO_LEX_H) #endif // !defined(GO_LEX_H)
...@@ -1305,10 +1305,10 @@ Parse::declaration() ...@@ -1305,10 +1305,10 @@ Parse::declaration()
{ {
const Token* token = this->peek_token(); const Token* token = this->peek_token();
bool saw_nointerface = this->lex_->get_and_clear_nointerface(); unsigned int pragmas = this->lex_->get_and_clear_pragmas();
if (saw_nointerface && !token->is_keyword(KEYWORD_FUNC)) if (pragmas != 0 && !token->is_keyword(KEYWORD_FUNC))
warning_at(token->location(), 0, warning_at(token->location(), 0,
"ignoring magic //go:nointerface comment before non-method"); "ignoring magic comment before non-function");
if (token->is_keyword(KEYWORD_CONST)) if (token->is_keyword(KEYWORD_CONST))
this->const_decl(); this->const_decl();
...@@ -1317,7 +1317,7 @@ Parse::declaration() ...@@ -1317,7 +1317,7 @@ Parse::declaration()
else if (token->is_keyword(KEYWORD_VAR)) else if (token->is_keyword(KEYWORD_VAR))
this->var_decl(); this->var_decl();
else if (token->is_keyword(KEYWORD_FUNC)) else if (token->is_keyword(KEYWORD_FUNC))
this->function_decl(saw_nointerface); this->function_decl(pragmas);
else else
{ {
error_at(this->location(), "expected declaration"); error_at(this->location(), "expected declaration");
...@@ -2236,13 +2236,12 @@ Parse::simple_var_decl_or_assignment(const std::string& name, ...@@ -2236,13 +2236,12 @@ Parse::simple_var_decl_or_assignment(const std::string& name,
// __asm__ "(" string_lit ")" . // __asm__ "(" string_lit ")" .
// This extension means a function whose real name is the identifier // This extension means a function whose real name is the identifier
// inside the asm. This extension will be removed at some future // inside the asm. This extension will be removed at some future
// date. It has been replaced with //extern comments. // date. It has been replaced with //extern or //go:linkname comments.
//
// SAW_NOINTERFACE is true if we saw a magic //go:nointerface comment, // PRAGMAS is a bitset of magic comments.
// which means that we omit the method from the type descriptor.
void void
Parse::function_decl(bool saw_nointerface) Parse::function_decl(unsigned int pragmas)
{ {
go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC)); go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC));
Location location = this->location(); Location location = this->location();
...@@ -2257,12 +2256,6 @@ Parse::function_decl(bool saw_nointerface) ...@@ -2257,12 +2256,6 @@ Parse::function_decl(bool saw_nointerface)
rec = this->receiver(); rec = this->receiver();
token = this->peek_token(); 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()) if (!token->is_identifier())
{ {
...@@ -2320,7 +2313,69 @@ Parse::function_decl(bool saw_nointerface) ...@@ -2320,7 +2313,69 @@ Parse::function_decl(bool saw_nointerface)
semi_loc)); semi_loc));
} }
if (!this->peek_token()->is_op(OPERATOR_LCURLY)) static struct {
unsigned int bit;
const char* name;
bool decl_ok;
bool func_ok;
bool method_ok;
} pragma_check[] =
{
{ GOPRAGMA_NOINTERFACE, "nointerface", false, false, true },
{ GOPRAGMA_NOESCAPE, "noescape", true, false, false },
{ GOPRAGMA_NORACE, "norace", false, true, true },
{ GOPRAGMA_NOSPLIT, "nosplit", false, true, true },
{ GOPRAGMA_NOINLINE, "noinline", false, true, true },
{ GOPRAGMA_SYSTEMSTACK, "systemstack", false, true, true },
{ GOPRAGMA_NOWRITEBARRIER, "nowritebarrier", false, true, true },
{ GOPRAGMA_NOWRITEBARRIERREC, "nowritebarrierrec", false, true, true },
{ GOPRAGMA_CGOUNSAFEARGS, "cgo_unsafe_args", false, true, true },
{ GOPRAGMA_UINTPTRESCAPES, "uintptrescapes", true, true, true },
};
bool is_decl = !this->peek_token()->is_op(OPERATOR_LCURLY);
if (pragmas != 0)
{
for (size_t i = 0;
i < sizeof(pragma_check) / sizeof(pragma_check[0]);
++i)
{
if ((pragmas & pragma_check[i].bit) == 0)
continue;
if (is_decl)
{
if (pragma_check[i].decl_ok)
continue;
warning_at(location, 0,
("ignoring magic //go:%s comment "
"before declaration"),
pragma_check[i].name);
}
else if (rec == NULL)
{
if (pragma_check[i].func_ok)
continue;
warning_at(location, 0,
("ignoring magic //go:%s comment "
"before function definition"),
pragma_check[i].name);
}
else
{
if (pragma_check[i].method_ok)
continue;
warning_at(location, 0,
("ignoring magic //go:%s comment "
"before method definition"),
pragma_check[i].name);
}
pragmas &= ~ pragma_check[i].bit;
}
}
if (is_decl)
{ {
if (named_object == NULL) if (named_object == NULL)
{ {
...@@ -2353,10 +2408,8 @@ Parse::function_decl(bool saw_nointerface) ...@@ -2353,10 +2408,8 @@ Parse::function_decl(bool saw_nointerface)
} }
} }
if (saw_nointerface) if (pragmas != 0 && named_object->is_function_declaration())
warning_at(location, 0, named_object->func_declaration_value()->set_pragmas(pragmas);
("ignoring magic //go:nointerface comment "
"before declaration"));
} }
else else
{ {
...@@ -2372,10 +2425,11 @@ Parse::function_decl(bool saw_nointerface) ...@@ -2372,10 +2425,11 @@ Parse::function_decl(bool saw_nointerface)
named_object = this->gogo_->start_function(name, fntype, true, location); named_object = this->gogo_->start_function(name, fntype, true, location);
Location end_loc = this->block(); Location end_loc = this->block();
this->gogo_->finish_function(end_loc); this->gogo_->finish_function(end_loc);
if (saw_nointerface
if (pragmas != 0
&& !this->is_erroneous_function_ && !this->is_erroneous_function_
&& named_object->is_function()) && named_object->is_function())
named_object->func_value()->set_nointerface(); named_object->func_value()->set_pragmas(pragmas);
this->is_erroneous_function_ = hold_is_erroneous_function; this->is_erroneous_function_ = hold_is_erroneous_function;
} }
} }
......
...@@ -209,7 +209,7 @@ class Parse ...@@ -209,7 +209,7 @@ class Parse
void simple_var_decl_or_assignment(const std::string&, Location, void simple_var_decl_or_assignment(const std::string&, Location,
bool may_be_composite_lit, bool may_be_composite_lit,
Range_clause*, Type_switch*); Range_clause*, Type_switch*);
void function_decl(bool saw_nointerface); void function_decl(unsigned int pragmas);
Typed_identifier* receiver(); Typed_identifier* receiver();
Expression* operand(bool may_be_sink, bool *is_parenthesized); Expression* operand(bool may_be_sink, bool *is_parenthesized);
Expression* enclosing_var_reference(Named_object*, Named_object*, Expression* enclosing_var_reference(Named_object*, Named_object*,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment