Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
R
riscv-gcc-1
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
riscv-gcc-1
Commits
571d3f91
Commit
571d3f91
authored
Jun 24, 2013
by
Ian Lance Taylor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
compiler: Add support for method values.
From-SVN: r200379
parent
39953c79
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
699 additions
and
161 deletions
+699
-161
gcc/go/gofrontend/expressions.cc
+525
-56
gcc/go/gofrontend/expressions.h
+52
-13
gcc/go/gofrontend/gogo.cc
+34
-71
gcc/go/gofrontend/statements.cc
+22
-0
gcc/go/gofrontend/statements.h
+7
-0
gcc/go/gofrontend/types.cc
+53
-21
gcc/go/gofrontend/types.h
+6
-0
No files found.
gcc/go/gofrontend/expressions.cc
View file @
571d3f91
...
...
@@ -1090,6 +1090,15 @@ Set_and_use_temporary_expression::do_type()
return
this
->
statement_
->
type
();
}
// Determine the type of the expression.
void
Set_and_use_temporary_expression
::
do_determine_type
(
const
Type_context
*
context
)
{
this
->
expr_
->
determine_type
(
context
);
}
// Take the address.
void
...
...
@@ -6626,20 +6635,49 @@ Bound_method_expression::do_traverse(Traverse* traverse)
return
Expression
::
traverse
(
&
this
->
expr_
,
traverse
);
}
// Lower the expression. If this is a method value rather than being
// called, and the method is accessed via a pointer, we may need to
// add nil checks. Introduce a temporary variable so that those nil
// checks do not cause multiple evaluation.
Expression
*
Bound_method_expression
::
do_lower
(
Gogo
*
,
Named_object
*
,
Statement_inserter
*
inserter
,
int
)
{
// For simplicity we use a temporary for every call to an embedded
// method, even though some of them might be pure value methods and
// not require a temporary.
if
(
this
->
expr_
->
var_expression
()
==
NULL
&&
this
->
expr_
->
temporary_reference_expression
()
==
NULL
&&
this
->
expr_
->
set_and_use_temporary_expression
()
==
NULL
&&
(
this
->
method_
->
field_indexes
()
!=
NULL
||
(
this
->
method_
->
is_value_method
()
&&
this
->
expr_
->
type
()
->
points_to
()
!=
NULL
)))
{
Temporary_statement
*
temp
=
Statement
::
make_temporary
(
this
->
expr_
->
type
(),
NULL
,
this
->
location
());
inserter
->
insert
(
temp
);
this
->
expr_
=
Expression
::
make_set_and_use_temporary
(
temp
,
this
->
expr_
,
this
->
location
());
}
return
this
;
}
// Return the type of a bound method expression. The type of this
// object is really the type of the method with no receiver. We
// should be able to get away with just returning the type of the
// method.
// object is simply the type of the method with no receiver.
Type
*
Bound_method_expression
::
do_type
()
{
if
(
this
->
method_
->
is_function
())
return
this
->
method_
->
func_value
()
->
type
();
else
if
(
this
->
method_
->
is_function_declaration
())
return
this
->
method_
->
func_declaration_value
()
->
type
();
Named_object
*
fn
=
this
->
method_
->
named_object
();
Function_type
*
fntype
;
if
(
fn
->
is_function
())
fntype
=
fn
->
func_value
()
->
type
();
else
if
(
fn
->
is_function_declaration
())
fntype
=
fn
->
func_declaration_value
()
->
type
();
else
return
Type
::
make_error_type
();
return
fntype
->
copy_without_receiver
();
}
// Determine the types of a method expression.
...
...
@@ -6647,7 +6685,14 @@ Bound_method_expression::do_type()
void
Bound_method_expression
::
do_determine_type
(
const
Type_context
*
)
{
Function_type
*
fntype
=
this
->
type
()
->
function_type
();
Named_object
*
fn
=
this
->
method_
->
named_object
();
Function_type
*
fntype
;
if
(
fn
->
is_function
())
fntype
=
fn
->
func_value
()
->
type
();
else
if
(
fn
->
is_function_declaration
())
fntype
=
fn
->
func_declaration_value
()
->
type
();
else
fntype
=
NULL
;
if
(
fntype
==
NULL
||
!
fntype
->
is_method
())
this
->
expr_
->
determine_type_no_context
();
else
...
...
@@ -6662,31 +6707,278 @@ Bound_method_expression::do_determine_type(const Type_context*)
void
Bound_method_expression
::
do_check_types
(
Gogo
*
)
{
if
(
!
this
->
method_
->
is_function
()
&&
!
this
->
method_
->
is_function_declaration
())
this
->
report_error
(
_
(
"object is not a method"
));
Named_object
*
fn
=
this
->
method_
->
named_object
();
if
(
!
fn
->
is_function
()
&&
!
fn
->
is_function_declaration
())
{
this
->
report_error
(
_
(
"object is not a method"
));
return
;
}
Function_type
*
fntype
;
if
(
fn
->
is_function
())
fntype
=
fn
->
func_value
()
->
type
();
else
if
(
fn
->
is_function_declaration
())
fntype
=
fn
->
func_declaration_value
()
->
type
();
else
go_unreachable
();
Type
*
rtype
=
fntype
->
receiver
()
->
type
()
->
deref
();
Type
*
etype
=
(
this
->
expr_type_
!=
NULL
?
this
->
expr_type_
:
this
->
expr_
->
type
());
etype
=
etype
->
deref
();
if
(
!
Type
::
are_identical
(
rtype
,
etype
,
true
,
NULL
))
this
->
report_error
(
_
(
"method type does not match object type"
));
}
// If a bound method expression is not simply called, then it is
// represented as a closure. The closure will hold a single variable,
// the receiver to pass to the method. The function will be a simple
// thunk that pulls that value from the closure and calls the method
// with the remaining arguments.
//
// Because method values are not common, we don't build all thunks for
// every methods, but instead only build them as we need them. In
// particular, we even build them on demand for methods defined in
// other packages.
Bound_method_expression
::
Method_value_thunks
Bound_method_expression
::
method_value_thunks
;
// Find or create the thunk for METHOD.
Named_object
*
Bound_method_expression
::
create_thunk
(
Gogo
*
gogo
,
const
Method
*
method
,
Named_object
*
fn
)
{
std
::
pair
<
Named_object
*
,
Named_object
*>
val
(
fn
,
NULL
);
std
::
pair
<
Method_value_thunks
::
iterator
,
bool
>
ins
=
Bound_method_expression
::
method_value_thunks
.
insert
(
val
);
if
(
!
ins
.
second
)
{
// We have seen this method before.
go_assert
(
ins
.
first
->
second
!=
NULL
);
return
ins
.
first
->
second
;
}
Location
loc
=
fn
->
location
();
Function_type
*
orig_fntype
;
if
(
fn
->
is_function
())
orig_fntype
=
fn
->
func_value
()
->
type
();
else
if
(
fn
->
is_function_declaration
())
orig_fntype
=
fn
->
func_declaration_value
()
->
type
();
else
orig_fntype
=
NULL
;
if
(
orig_fntype
==
NULL
||
!
orig_fntype
->
is_method
())
{
ins
.
first
->
second
=
Named_object
::
make_erroneous_name
(
Gogo
::
thunk_name
());
return
ins
.
first
->
second
;
}
Struct_field_list
*
sfl
=
new
Struct_field_list
();
// The type here is wrong--it should be new_fntype. But we don't
// have new_fntype yet, and it doesn't really matter.
Type
*
vt
=
Type
::
make_pointer_type
(
Type
::
make_void_type
());
sfl
->
push_back
(
Struct_field
(
Typed_identifier
(
"fn.0"
,
vt
,
loc
)));
sfl
->
push_back
(
Struct_field
(
Typed_identifier
(
"val.1"
,
orig_fntype
->
receiver
()
->
type
(),
loc
)));
Type
*
closure_type
=
Type
::
make_struct_type
(
sfl
,
loc
);
closure_type
=
Type
::
make_pointer_type
(
closure_type
);
Function_type
*
new_fntype
=
orig_fntype
->
copy_with_closure
(
closure_type
);
Named_object
*
new_no
=
gogo
->
start_function
(
Gogo
::
thunk_name
(),
new_fntype
,
false
,
loc
);
gogo
->
start_block
(
loc
);
Named_object
*
cp
=
gogo
->
lookup
(
"closure.0"
,
NULL
);
go_assert
(
cp
!=
NULL
&&
cp
->
is_variable
()
&&
cp
->
var_value
()
->
is_parameter
());
// Field 0 of the closure is the function code pointer, field 1 is
// the value on which to invoke the method.
Expression
*
arg
=
Expression
::
make_var_reference
(
cp
,
loc
);
arg
=
Expression
::
make_unary
(
OPERATOR_MULT
,
arg
,
loc
);
arg
=
Expression
::
make_field_reference
(
arg
,
1
,
loc
);
Expression
*
bme
=
Expression
::
make_bound_method
(
arg
,
method
,
fn
,
loc
);
const
Typed_identifier_list
*
orig_params
=
orig_fntype
->
parameters
();
Expression_list
*
args
;
if
(
orig_params
==
NULL
||
orig_params
->
empty
())
args
=
NULL
;
else
{
Type
*
rtype
=
this
->
type
()
->
function_type
()
->
receiver
()
->
type
()
->
deref
();
Type
*
etype
=
(
this
->
expr_type_
!=
NULL
?
this
->
expr_type_
:
this
->
expr_
->
type
());
etype
=
etype
->
deref
();
if
(
!
Type
::
are_identical
(
rtype
,
etype
,
true
,
NULL
))
this
->
report_error
(
_
(
"method type does not match object type"
));
const
Typed_identifier_list
*
new_params
=
new_fntype
->
parameters
();
args
=
new
Expression_list
();
for
(
Typed_identifier_list
::
const_iterator
p
=
new_params
->
begin
();
p
+
1
!=
new_params
->
end
();
++
p
)
{
Named_object
*
p_no
=
gogo
->
lookup
(
p
->
name
(),
NULL
);
go_assert
(
p_no
!=
NULL
&&
p_no
->
is_variable
()
&&
p_no
->
var_value
()
->
is_parameter
());
args
->
push_back
(
Expression
::
make_var_reference
(
p_no
,
loc
));
}
}
Call_expression
*
call
=
Expression
::
make_call
(
bme
,
args
,
orig_fntype
->
is_varargs
(),
loc
);
call
->
set_varargs_are_lowered
();
Statement
*
s
=
Statement
::
make_return_from_call
(
call
,
loc
);
gogo
->
add_statement
(
s
);
Block
*
b
=
gogo
->
finish_block
(
loc
);
gogo
->
add_block
(
b
,
loc
);
gogo
->
lower_block
(
new_no
,
b
);
gogo
->
finish_function
(
loc
);
ins
.
first
->
second
=
new_no
;
return
new_no
;
}
// Return an expression to check *REF for nil while dereferencing
// according to FIELD_INDEXES. Update *REF to build up the field
// reference. This is a static function so that we don't have to
// worry about declaring Field_indexes in expressions.h.
static
Expression
*
bme_check_nil
(
const
Method
::
Field_indexes
*
field_indexes
,
Location
loc
,
Expression
**
ref
)
{
if
(
field_indexes
==
NULL
)
return
Expression
::
make_boolean
(
false
,
loc
);
Expression
*
cond
=
bme_check_nil
(
field_indexes
->
next
,
loc
,
ref
);
Struct_type
*
stype
=
(
*
ref
)
->
type
()
->
deref
()
->
struct_type
();
go_assert
(
stype
!=
NULL
&&
field_indexes
->
field_index
<
stype
->
field_count
());
if
((
*
ref
)
->
type
()
->
struct_type
()
==
NULL
)
{
go_assert
((
*
ref
)
->
type
()
->
points_to
()
!=
NULL
);
Expression
*
n
=
Expression
::
make_binary
(
OPERATOR_EQEQ
,
*
ref
,
Expression
::
make_nil
(
loc
),
loc
);
cond
=
Expression
::
make_binary
(
OPERATOR_OROR
,
cond
,
n
,
loc
);
*
ref
=
Expression
::
make_unary
(
OPERATOR_MULT
,
*
ref
,
loc
);
go_assert
((
*
ref
)
->
type
()
->
struct_type
()
==
stype
);
}
*
ref
=
Expression
::
make_field_reference
(
*
ref
,
field_indexes
->
field_index
,
loc
);
return
cond
;
}
// Get the tree for a method expression. There is no standard tree
// representation for this. The only places it may currently be used
// are in a Call_expression or a Go_statement, which will take it
// apart directly. So this has nothing to do at present.
// Get the tree for a method value.
tree
Bound_method_expression
::
do_get_tree
(
Translate_context
*
)
Bound_method_expression
::
do_get_tree
(
Translate_context
*
context
)
{
error_at
(
this
->
location
(),
"reference to method other than calling it"
);
return
error_mark_node
;
Named_object
*
thunk
=
Bound_method_expression
::
create_thunk
(
context
->
gogo
(),
this
->
method_
,
this
->
function_
);
if
(
thunk
->
is_erroneous
())
{
go_assert
(
saw_errors
());
return
error_mark_node
;
}
// FIXME: We should lower this earlier, but we can't lower it in the
// lowering pass because at that point we don't know whether we need
// to create the thunk or not. If the expression is called, we
// don't need the thunk.
Location
loc
=
this
->
location
();
// If the method expects a value, and we have a pointer, we need to
// dereference the pointer.
Named_object
*
fn
=
this
->
method_
->
named_object
();
Function_type
*
fntype
;
if
(
fn
->
is_function
())
fntype
=
fn
->
func_value
()
->
type
();
else
if
(
fn
->
is_function_declaration
())
fntype
=
fn
->
func_declaration_value
()
->
type
();
else
go_unreachable
();
Expression
*
val
=
this
->
expr_
;
if
(
fntype
->
receiver
()
->
type
()
->
points_to
()
==
NULL
&&
val
->
type
()
->
points_to
()
!=
NULL
)
val
=
Expression
::
make_unary
(
OPERATOR_MULT
,
val
,
loc
);
// Note that we are ignoring this->expr_type_ here. The thunk will
// expect a closure whose second field has type this->expr_type_ (if
// that is not NULL). We are going to pass it a closure whose
// second field has type this->expr_->type(). Since
// this->expr_type_ is only not-NULL for pointer types, we can get
// away with this.
Struct_field_list
*
fields
=
new
Struct_field_list
();
fields
->
push_back
(
Struct_field
(
Typed_identifier
(
"fn.0"
,
thunk
->
func_value
()
->
type
(),
loc
)));
fields
->
push_back
(
Struct_field
(
Typed_identifier
(
"val.1"
,
val
->
type
(),
loc
)));
Struct_type
*
st
=
Type
::
make_struct_type
(
fields
,
loc
);
Expression_list
*
vals
=
new
Expression_list
();
vals
->
push_back
(
Expression
::
make_func_code_reference
(
thunk
,
loc
));
vals
->
push_back
(
val
);
Expression
*
ret
=
Expression
::
make_struct_composite_literal
(
st
,
vals
,
loc
);
ret
=
Expression
::
make_heap_composite
(
ret
,
loc
);
tree
ret_tree
=
ret
->
get_tree
(
context
);
Expression
*
nil_check
=
NULL
;
// See whether the expression or any embedded pointers are nil.
Expression
*
expr
=
this
->
expr_
;
if
(
this
->
method_
->
field_indexes
()
!=
NULL
)
{
// Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary
// variable.
Expression
*
ref
=
expr
;
nil_check
=
bme_check_nil
(
this
->
method_
->
field_indexes
(),
loc
,
&
ref
);
expr
=
ref
;
}
if
(
this
->
method_
->
is_value_method
()
&&
expr
->
type
()
->
points_to
()
!=
NULL
)
{
Expression
*
n
=
Expression
::
make_binary
(
OPERATOR_EQEQ
,
expr
,
Expression
::
make_nil
(
loc
),
loc
);
if
(
nil_check
==
NULL
)
nil_check
=
n
;
else
nil_check
=
Expression
::
make_binary
(
OPERATOR_OROR
,
nil_check
,
n
,
loc
);
}
if
(
nil_check
!=
NULL
)
{
tree
nil_check_tree
=
nil_check
->
get_tree
(
context
);
tree
crash
=
context
->
gogo
()
->
runtime_error
(
RUNTIME_ERROR_NIL_DEREFERENCE
,
loc
);
if
(
ret_tree
==
error_mark_node
||
nil_check_tree
==
error_mark_node
||
crash
==
error_mark_node
)
return
error_mark_node
;
ret_tree
=
fold_build2_loc
(
loc
.
gcc_location
(),
COMPOUND_EXPR
,
TREE_TYPE
(
ret_tree
),
build3_loc
(
loc
.
gcc_location
(),
COND_EXPR
,
void_type_node
,
nil_check_tree
,
crash
,
NULL_TREE
),
ret_tree
);
}
return
ret_tree
;
}
// Dump ast representation of a bound method expression.
...
...
@@ -6705,16 +6997,16 @@ Bound_method_expression::do_dump_expression(Ast_dump_context* ast_dump_context)
ast_dump_context
->
ostream
()
<<
")"
;
}
ast_dump_context
->
ostream
()
<<
"."
<<
this
->
method
_
->
name
();
ast_dump_context
->
ostream
()
<<
"."
<<
this
->
function
_
->
name
();
}
// Make a method expression.
Bound_method_expression
*
Expression
::
make_bound_method
(
Expression
*
expr
,
Named_object
*
method
,
Location
location
)
Expression
::
make_bound_method
(
Expression
*
expr
,
const
Method
*
method
,
Named_object
*
function
,
Location
location
)
{
return
new
Bound_method_expression
(
expr
,
method
,
location
);
return
new
Bound_method_expression
(
expr
,
method
,
function
,
location
);
}
// Class Builtin_call_expression. This is used for a call to a
...
...
@@ -8921,7 +9213,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
Bound_method_expression
*
bme
=
this
->
fn_
->
bound_method_expression
();
if
(
bme
!=
NULL
)
{
Named_object
*
method
=
bme
->
method
();
Named_object
*
method
fn
=
bme
->
function
();
Expression
*
first_arg
=
bme
->
first_argument
();
// We always pass a pointer when calling a method.
...
...
@@ -8962,7 +9254,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
// old arguments, because we may be traversing them up in some
// caller. FIXME.
this
->
args_
=
new_args
;
this
->
fn_
=
Expression
::
make_func_reference
(
method
,
NULL
,
this
->
fn_
=
Expression
::
make_func_reference
(
method
fn
,
NULL
,
bme
->
location
());
}
...
...
@@ -11158,6 +11450,28 @@ Interface_field_reference_expression::do_traverse(Traverse* traverse)
return
Expression
::
traverse
(
&
this
->
expr_
,
traverse
);
}
// Lower the expression. If this expression is not called, we need to
// evaluate the expression twice when converting to the backend
// interface. So introduce a temporary variable if necessary.
Expression
*
Interface_field_reference_expression
::
do_lower
(
Gogo
*
,
Named_object
*
,
Statement_inserter
*
inserter
,
int
)
{
if
(
this
->
expr_
->
var_expression
()
==
NULL
&&
this
->
expr_
->
temporary_reference_expression
()
==
NULL
&&
this
->
expr_
->
set_and_use_temporary_expression
()
==
NULL
)
{
Temporary_statement
*
temp
=
Statement
::
make_temporary
(
this
->
expr_
->
type
(),
NULL
,
this
->
location
());
inserter
->
insert
(
temp
);
this
->
expr_
=
Expression
::
make_set_and_use_temporary
(
temp
,
this
->
expr_
,
this
->
location
());
}
return
this
;
}
// Return the type of an interface field reference.
Type
*
...
...
@@ -11218,18 +11532,188 @@ Interface_field_reference_expression::do_check_types(Gogo*)
}
}
// Get a tree for a reference to a field in an interface. There is no
// standard tree type representation for this: it's a function
// attached to its first argument, like a Bound_method_expression.
// The only places it may currently be used are in a Call_expression
// or a Go_statement, which will take it apart directly. So this has
// nothing to do at present.
// If an interface field reference is not simply called, then it is
// represented as a closure. The closure will hold a single variable,
// the value of the interface on which the method should be called.
// The function will be a simple thunk that pulls the value from the
// closure and calls the method with the remaining arguments.
// Because method values are not common, we don't build all thunks for
// all possible interface methods, but instead only build them as we
// need them. In particular, we even build them on demand for
// interface methods defined in other packages.
Interface_field_reference_expression
::
Interface_method_thunks
Interface_field_reference_expression
::
interface_method_thunks
;
// Find or create the thunk to call method NAME on TYPE.
Named_object
*
Interface_field_reference_expression
::
create_thunk
(
Gogo
*
gogo
,
Interface_type
*
type
,
const
std
::
string
&
name
)
{
std
::
pair
<
Interface_type
*
,
Method_thunks
*>
val
(
type
,
NULL
);
std
::
pair
<
Interface_method_thunks
::
iterator
,
bool
>
ins
=
Interface_field_reference_expression
::
interface_method_thunks
.
insert
(
val
);
if
(
ins
.
second
)
{
// This is the first time we have seen this interface.
ins
.
first
->
second
=
new
Method_thunks
();
}
for
(
Method_thunks
::
const_iterator
p
=
ins
.
first
->
second
->
begin
();
p
!=
ins
.
first
->
second
->
end
();
p
++
)
if
(
p
->
first
==
name
)
return
p
->
second
;
Location
loc
=
type
->
location
();
const
Typed_identifier
*
method_id
=
type
->
find_method
(
name
);
if
(
method_id
==
NULL
)
return
Named_object
::
make_erroneous_name
(
Gogo
::
thunk_name
());
Function_type
*
orig_fntype
=
method_id
->
type
()
->
function_type
();
if
(
orig_fntype
==
NULL
)
return
Named_object
::
make_erroneous_name
(
Gogo
::
thunk_name
());
Struct_field_list
*
sfl
=
new
Struct_field_list
();
// The type here is wrong--it should be new_fntype. But we don't
// have new_fntype yet, and it doesn't really matter.
Type
*
vt
=
Type
::
make_pointer_type
(
Type
::
make_void_type
());
sfl
->
push_back
(
Struct_field
(
Typed_identifier
(
"fn.0"
,
vt
,
loc
)));
sfl
->
push_back
(
Struct_field
(
Typed_identifier
(
"val.1"
,
type
,
loc
)));
Type
*
closure_type
=
Type
::
make_struct_type
(
sfl
,
loc
);
closure_type
=
Type
::
make_pointer_type
(
closure_type
);
Function_type
*
new_fntype
=
orig_fntype
->
copy_with_closure
(
closure_type
);
Named_object
*
new_no
=
gogo
->
start_function
(
Gogo
::
thunk_name
(),
new_fntype
,
false
,
loc
);
gogo
->
start_block
(
loc
);
Named_object
*
cp
=
gogo
->
lookup
(
"closure.0"
,
NULL
);
go_assert
(
cp
!=
NULL
&&
cp
->
is_variable
()
&&
cp
->
var_value
()
->
is_parameter
());
// Field 0 of the closure is the function code pointer, field 1 is
// the value on which to invoke the method.
Expression
*
arg
=
Expression
::
make_var_reference
(
cp
,
loc
);
arg
=
Expression
::
make_unary
(
OPERATOR_MULT
,
arg
,
loc
);
arg
=
Expression
::
make_field_reference
(
arg
,
1
,
loc
);
Expression
*
ifre
=
Expression
::
make_interface_field_reference
(
arg
,
name
,
loc
);
const
Typed_identifier_list
*
orig_params
=
orig_fntype
->
parameters
();
Expression_list
*
args
;
if
(
orig_params
==
NULL
||
orig_params
->
empty
())
args
=
NULL
;
else
{
const
Typed_identifier_list
*
new_params
=
new_fntype
->
parameters
();
args
=
new
Expression_list
();
for
(
Typed_identifier_list
::
const_iterator
p
=
new_params
->
begin
();
p
+
1
!=
new_params
->
end
();
++
p
)
{
Named_object
*
p_no
=
gogo
->
lookup
(
p
->
name
(),
NULL
);
go_assert
(
p_no
!=
NULL
&&
p_no
->
is_variable
()
&&
p_no
->
var_value
()
->
is_parameter
());
args
->
push_back
(
Expression
::
make_var_reference
(
p_no
,
loc
));
}
}
Call_expression
*
call
=
Expression
::
make_call
(
ifre
,
args
,
orig_fntype
->
is_varargs
(),
loc
);
call
->
set_varargs_are_lowered
();
Statement
*
s
=
Statement
::
make_return_from_call
(
call
,
loc
);
gogo
->
add_statement
(
s
);
Block
*
b
=
gogo
->
finish_block
(
loc
);
gogo
->
add_block
(
b
,
loc
);
gogo
->
lower_block
(
new_no
,
b
);
gogo
->
finish_function
(
loc
);
ins
.
first
->
second
->
push_back
(
std
::
make_pair
(
name
,
new_no
));
return
new_no
;
}
// Get a tree for a method value.
tree
Interface_field_reference_expression
::
do_get_tree
(
Translate_context
*
)
Interface_field_reference_expression
::
do_get_tree
(
Translate_context
*
context
)
{
error_at
(
this
->
location
(),
"reference to method other than calling it"
);
return
error_mark_node
;
Interface_type
*
type
=
this
->
expr_
->
type
()
->
interface_type
();
if
(
type
==
NULL
)
{
go_assert
(
saw_errors
());
return
error_mark_node
;
}
Named_object
*
thunk
=
Interface_field_reference_expression
::
create_thunk
(
context
->
gogo
(),
type
,
this
->
name_
);
if
(
thunk
->
is_erroneous
())
{
go_assert
(
saw_errors
());
return
error_mark_node
;
}
// FIXME: We should lower this earlier, but we can't it lower it in
// the lowering pass because at that point we don't know whether we
// need to create the thunk or not. If the expression is called, we
// don't need the thunk.
Location
loc
=
this
->
location
();
Struct_field_list
*
fields
=
new
Struct_field_list
();
fields
->
push_back
(
Struct_field
(
Typed_identifier
(
"fn.0"
,
thunk
->
func_value
()
->
type
(),
loc
)));
fields
->
push_back
(
Struct_field
(
Typed_identifier
(
"val.1"
,
this
->
expr_
->
type
(),
loc
)));
Struct_type
*
st
=
Type
::
make_struct_type
(
fields
,
loc
);
Expression_list
*
vals
=
new
Expression_list
();
vals
->
push_back
(
Expression
::
make_func_code_reference
(
thunk
,
loc
));
vals
->
push_back
(
this
->
expr_
);
Expression
*
expr
=
Expression
::
make_struct_composite_literal
(
st
,
vals
,
loc
);
expr
=
Expression
::
make_heap_composite
(
expr
,
loc
);
tree
closure_tree
=
expr
->
get_tree
(
context
);
// Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary
// variable.
tree
expr_tree
=
this
->
expr_
->
get_tree
(
context
);
tree
nil_check_tree
=
Expression
::
comparison_tree
(
context
,
Type
::
lookup_bool_type
(),
OPERATOR_EQEQ
,
this
->
expr_
->
type
(),
expr_tree
,
Type
::
make_nil_type
(),
null_pointer_node
,
loc
);
tree
crash
=
context
->
gogo
()
->
runtime_error
(
RUNTIME_ERROR_NIL_DEREFERENCE
,
loc
);
if
(
closure_tree
==
error_mark_node
||
nil_check_tree
==
error_mark_node
||
crash
==
error_mark_node
)
return
error_mark_node
;
return
fold_build2_loc
(
loc
.
gcc_location
(),
COMPOUND_EXPR
,
TREE_TYPE
(
closure_tree
),
build3_loc
(
loc
.
gcc_location
(),
COND_EXPR
,
void_type_node
,
nil_check_tree
,
crash
,
NULL_TREE
),
closure_tree
);
}
// Dump ast representation for an interface field reference.
...
...
@@ -11485,22 +11969,7 @@ Selector_expression::lower_method_expression(Gogo* gogo)
method_type
->
is_varargs
(),
location
);
size_t
count
=
call
->
result_count
();
Statement
*
s
;
if
(
count
==
0
)
s
=
Statement
::
make_statement
(
call
,
true
);
else
{
Expression_list
*
retvals
=
new
Expression_list
();
if
(
count
<=
1
)
retvals
->
push_back
(
call
);
else
{
for
(
size_t
i
=
0
;
i
<
count
;
++
i
)
retvals
->
push_back
(
Expression
::
make_call_result
(
call
,
i
));
}
s
=
Statement
::
make_return_statement
(
retvals
,
location
);
}
Statement
*
s
=
Statement
::
make_return_from_call
(
call
,
location
);
gogo
->
add_statement
(
s
);
Block
*
b
=
gogo
->
finish_block
(
location
);
...
...
gcc/go/gofrontend/expressions.h
View file @
571d3f91
...
...
@@ -16,6 +16,7 @@ class Translate_context;
class
Traverse
;
class
Statement_inserter
;
class
Type
;
class
Method
;
struct
Type_context
;
class
Integer_type
;
class
Float_type
;
...
...
@@ -224,9 +225,11 @@ class Expression
make_call_result
(
Call_expression
*
,
unsigned
int
index
);
// Make an expression which is a method bound to its first
// parameter.
// parameter. METHOD is the method being called, FUNCTION is the
// function to call.
static
Bound_method_expression
*
make_bound_method
(
Expression
*
object
,
Named_object
*
method
,
Location
);
make_bound_method
(
Expression
*
object
,
const
Method
*
method
,
Named_object
*
function
,
Location
);
// Make an index or slice expression. This is a parser expression
// which represents LEFT[START:END]. END may be NULL, meaning an
...
...
@@ -1079,8 +1082,7 @@ class Set_and_use_temporary_expression : public Expression
do_type
();
void
do_determine_type
(
const
Type_context
*
)
{
}
do_determine_type
(
const
Type_context
*
);
Expression
*
do_copy
()
...
...
@@ -1852,10 +1854,10 @@ class Map_index_expression : public Expression
class
Bound_method_expression
:
public
Expression
{
public
:
Bound_method_expression
(
Expression
*
expr
,
Named_object
*
method
,
Location
location
)
Bound_method_expression
(
Expression
*
expr
,
const
Method
*
method
,
Named_object
*
function
,
Location
location
)
:
Expression
(
EXPRESSION_BOUND_METHOD
,
location
),
expr_
(
expr
),
expr_type_
(
NULL
),
method_
(
method
)
expr_
(
expr
),
expr_type_
(
NULL
),
method_
(
method
)
,
function_
(
function
)
{
}
// Return the object which is the first argument.
...
...
@@ -1870,20 +1872,33 @@ class Bound_method_expression : public Expression
first_argument_type
()
const
{
return
this
->
expr_type_
;
}
// Return the method
function
.
Named_object
*
method
()
// Return the method.
const
Method
*
method
()
const
{
return
this
->
method_
;
}
// Return the function to call.
Named_object
*
function
()
const
{
return
this
->
function_
;
}
// Set the implicit type of the expression.
void
set_first_argument_type
(
Type
*
type
)
{
this
->
expr_type_
=
type
;
}
// Create a thunk to call FUNCTION, for METHOD, when it is used as
// part of a method value.
static
Named_object
*
create_thunk
(
Gogo
*
,
const
Method
*
method
,
Named_object
*
function
);
protected
:
int
do_traverse
(
Traverse
*
);
Expression
*
do_lower
(
Gogo
*
,
Named_object
*
,
Statement_inserter
*
,
int
);
Type
*
do_type
();
...
...
@@ -1897,7 +1912,7 @@ class Bound_method_expression : public Expression
do_copy
()
{
return
new
Bound_method_expression
(
this
->
expr_
->
copy
(),
this
->
method_
,
this
->
location
());
this
->
function_
,
this
->
location
());
}
tree
...
...
@@ -1907,6 +1922,11 @@ class Bound_method_expression : public Expression
do_dump_expression
(
Ast_dump_context
*
)
const
;
private
:
// A mapping from method functions to the thunks we have created for
// them.
typedef
Unordered_map
(
Named_object
*
,
Named_object
*
)
Method_value_thunks
;
static
Method_value_thunks
method_value_thunks
;
// The object used to find the method. This is passed to the method
// as the first argument.
Expression
*
expr_
;
...
...
@@ -1914,8 +1934,12 @@ class Bound_method_expression : public Expression
// NULL in the normal case, non-NULL when using a method from an
// anonymous field which does not require a stub.
Type
*
expr_type_
;
// The method itself.
Named_object
*
method_
;
// The method.
const
Method
*
method_
;
// The function to call. This is not the same as
// method_->named_object() when the method has a stub. This will be
// the real function rather than the stub.
Named_object
*
function_
;
};
// A reference to a field in a struct.
...
...
@@ -2031,6 +2055,11 @@ class Interface_field_reference_expression : public Expression
name
()
const
{
return
this
->
name_
;
}
// Create a thunk to call the method NAME in TYPE when it is used as
// part of a method value.
static
Named_object
*
create_thunk
(
Gogo
*
,
Interface_type
*
type
,
const
std
::
string
&
name
);
// Return a tree for the pointer to the function to call, given a
// tree for the expression.
tree
...
...
@@ -2046,6 +2075,9 @@ class Interface_field_reference_expression : public Expression
int
do_traverse
(
Traverse
*
traverse
);
Expression
*
do_lower
(
Gogo
*
,
Named_object
*
,
Statement_inserter
*
,
int
);
Type
*
do_type
();
...
...
@@ -2070,6 +2102,13 @@ class Interface_field_reference_expression : public Expression
do_dump_expression
(
Ast_dump_context
*
)
const
;
private
:
// A mapping from interface types to a list of thunks we have
// created for methods.
typedef
std
::
vector
<
std
::
pair
<
std
::
string
,
Named_object
*>
>
Method_thunks
;
typedef
Unordered_map
(
Interface_type
*
,
Method_thunks
*
)
Interface_method_thunks
;
static
Interface_method_thunks
interface_method_thunks
;
// The expression for the interface object. This should have a type
// of interface or pointer to interface.
Expression
*
expr_
;
...
...
gcc/go/gofrontend/gogo.cc
View file @
571d3f91
...
...
@@ -1795,11 +1795,36 @@ Create_function_descriptors::expression(Expression** pexpr)
return
TRAVERSE_CONTINUE
;
}
Bound_method_expression
*
bme
=
expr
->
bound_method_expression
();
if
(
bme
!=
NULL
)
{
// We would not get here for a call to this method, so this is a
// method value. We need to create a thunk.
Bound_method_expression
::
create_thunk
(
this
->
gogo_
,
bme
->
method
(),
bme
->
function
());
return
TRAVERSE_CONTINUE
;
}
Interface_field_reference_expression
*
ifre
=
expr
->
interface_field_reference_expression
();
if
(
ifre
!=
NULL
)
{
// We would not get here for a call to this interface method, so
// this is a method value. We need to create a thunk.
Interface_type
*
type
=
ifre
->
expr
()
->
type
()
->
interface_type
();
if
(
type
!=
NULL
)
Interface_field_reference_expression
::
create_thunk
(
this
->
gogo_
,
type
,
ifre
->
name
());
return
TRAVERSE_CONTINUE
;
}
Call_expression
*
ce
=
expr
->
call_expression
();
if
(
ce
!=
NULL
)
{
Expression
*
fn
=
ce
->
fn
();
if
(
fn
->
func_expression
()
!=
NULL
)
if
(
fn
->
func_expression
()
!=
NULL
||
fn
->
bound_method_expression
()
!=
NULL
||
fn
->
interface_field_reference_expression
()
!=
NULL
)
{
// Traverse the arguments but not the function.
Expression_list
*
args
=
ce
->
args
();
...
...
@@ -2806,22 +2831,7 @@ Build_recover_thunks::function(Named_object* orig_no)
// Any varargs call has already been lowered.
call
->
set_varargs_are_lowered
();
Statement
*
s
;
if
(
orig_fntype
->
results
()
==
NULL
||
orig_fntype
->
results
()
->
empty
())
s
=
Statement
::
make_statement
(
call
,
true
);
else
{
Expression_list
*
vals
=
new
Expression_list
();
size_t
rc
=
orig_fntype
->
results
()
->
size
();
if
(
rc
==
1
)
vals
->
push_back
(
call
);
else
{
for
(
size_t
i
=
0
;
i
<
rc
;
++
i
)
vals
->
push_back
(
Expression
::
make_call_result
(
call
,
i
));
}
s
=
Statement
::
make_return_statement
(
vals
,
location
);
}
Statement
*
s
=
Statement
::
make_return_from_call
(
call
,
location
);
s
->
determine_types
();
gogo
->
add_statement
(
s
);
...
...
@@ -3557,42 +3567,8 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
{
Location
loc
=
no
->
location
();
Typed_identifier_list
*
new_params
=
new
Typed_identifier_list
();
const
Typed_identifier_list
*
orig_params
=
orig_fntype
->
parameters
();
if
(
orig_params
!=
NULL
&&
!
orig_params
->
empty
())
{
static
int
count
;
char
buf
[
50
];
for
(
Typed_identifier_list
::
const_iterator
p
=
orig_params
->
begin
();
p
!=
orig_params
->
end
();
++
p
)
{
snprintf
(
buf
,
sizeof
buf
,
"pt.%u"
,
count
);
++
count
;
new_params
->
push_back
(
Typed_identifier
(
buf
,
p
->
type
(),
p
->
location
()));
}
}
Type
*
vt
=
Type
::
make_pointer_type
(
Type
::
make_void_type
());
new_params
->
push_back
(
Typed_identifier
(
"closure.0"
,
vt
,
loc
));
const
Typed_identifier_list
*
orig_results
=
orig_fntype
->
results
();
Typed_identifier_list
*
new_results
;
if
(
orig_results
==
NULL
||
orig_results
->
empty
())
new_results
=
NULL
;
else
{
new_results
=
new
Typed_identifier_list
();
for
(
Typed_identifier_list
::
const_iterator
p
=
orig_results
->
begin
();
p
!=
orig_results
->
end
();
++
p
)
new_results
->
push_back
(
Typed_identifier
(
""
,
p
->
type
(),
p
->
location
()));
}
Function_type
*
new_fntype
=
Type
::
make_function_type
(
NULL
,
new_params
,
new_results
,
loc
);
Function_type
*
new_fntype
=
orig_fntype
->
copy_with_closure
(
vt
);
std
::
string
name
=
no
->
name
()
+
"$descriptorfn"
;
Named_object
*
dno
=
gogo
->
start_function
(
name
,
new_fntype
,
false
,
loc
);
...
...
@@ -3602,13 +3578,16 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
Expression
*
fn
=
Expression
::
make_func_reference
(
no
,
NULL
,
loc
);
// Call the wrapper function, passing all of the arguments except
// for the last one (the last argument is the ignored closure).
// Call the function begin wrapped, passing all of the arguments
// except for the last one (the last argument is the ignored
// closure).
const
Typed_identifier_list
*
orig_params
=
orig_fntype
->
parameters
();
Expression_list
*
args
;
if
(
orig_params
==
NULL
||
orig_params
->
empty
())
args
=
NULL
;
else
{
const
Typed_identifier_list
*
new_params
=
new_fntype
->
parameters
();
args
=
new
Expression_list
();
for
(
Typed_identifier_list
::
const_iterator
p
=
new_params
->
begin
();
p
+
1
!=
new_params
->
end
();
...
...
@@ -3627,23 +3606,7 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
loc
);
call
->
set_varargs_are_lowered
();
Statement
*
s
;
if
(
orig_results
==
NULL
||
orig_results
->
empty
())
s
=
Statement
::
make_statement
(
call
,
true
);
else
{
Expression_list
*
vals
=
new
Expression_list
();
size_t
rc
=
orig_results
->
size
();
if
(
rc
==
1
)
vals
->
push_back
(
call
);
else
{
for
(
size_t
i
=
0
;
i
<
rc
;
++
i
)
vals
->
push_back
(
Expression
::
make_call_result
(
call
,
i
));
}
s
=
Statement
::
make_return_statement
(
vals
,
loc
);
}
Statement
*
s
=
Statement
::
make_return_from_call
(
call
,
loc
);
gogo
->
add_statement
(
s
);
Block
*
b
=
gogo
->
finish_block
(
loc
);
gogo
->
add_block
(
b
,
loc
);
...
...
gcc/go/gofrontend/statements.cc
View file @
571d3f91
...
...
@@ -2815,6 +2815,28 @@ Statement::make_return_statement(Expression_list* vals,
return
new
Return_statement
(
vals
,
location
);
}
// Make a statement that returns the result of a call expression.
Statement
*
Statement
::
make_return_from_call
(
Call_expression
*
call
,
Location
location
)
{
size_t
rc
=
call
->
result_count
();
if
(
rc
==
0
)
return
Statement
::
make_statement
(
call
,
true
);
else
{
Expression_list
*
vals
=
new
Expression_list
();
if
(
rc
==
1
)
vals
->
push_back
(
call
);
else
{
for
(
size_t
i
=
0
;
i
<
rc
;
++
i
)
vals
->
push_back
(
Expression
::
make_call_result
(
call
,
i
));
}
return
Statement
::
make_return_statement
(
vals
,
location
);
}
}
// A break or continue statement.
class
Bc_statement
:
public
Statement
...
...
gcc/go/gofrontend/statements.h
View file @
571d3f91
...
...
@@ -207,6 +207,13 @@ class Statement
static
Return_statement
*
make_return_statement
(
Expression_list
*
,
Location
);
// Make a statement that returns the result of a call expression.
// If the call does not return any results, this just returns the
// call expression as a statement, assuming that the function will
// end immediately afterward.
static
Statement
*
make_return_from_call
(
Call_expression
*
,
Location
);
// Make a break statement.
static
Statement
*
make_break_statement
(
Unnamed_label
*
label
,
Location
);
...
...
gcc/go/gofrontend/types.cc
View file @
571d3f91
...
...
@@ -3396,7 +3396,8 @@ Function_type::do_get_backend(Gogo* gogo)
// passed when invoking the function indirectly, via the struct.
Location
loc
=
this
->
location
();
Btype
*
struct_type
=
gogo
->
backend
()
->
placeholder_struct_type
(
""
,
loc
);
Btype
*
struct_type
=
gogo
->
backend
()
->
placeholder_struct_type
(
"__go_descriptor"
,
loc
);
Btype
*
ptr_struct_type
=
gogo
->
backend
()
->
pointer_type
(
struct_type
);
Backend
::
Btyped_identifier
breceiver
;
...
...
@@ -3835,6 +3836,49 @@ Function_type::copy_with_receiver(Type* receiver_type) const
return
ret
;
}
// Make a copy of a function type ignoring any receiver and adding a
// closure parameter.
Function_type
*
Function_type
::
copy_with_closure
(
Type
*
closure_type
)
const
{
Typed_identifier_list
*
new_params
=
new
Typed_identifier_list
();
const
Typed_identifier_list
*
orig_params
=
this
->
parameters_
;
if
(
orig_params
!=
NULL
&&
!
orig_params
->
empty
())
{
static
int
count
;
char
buf
[
50
];
for
(
Typed_identifier_list
::
const_iterator
p
=
orig_params
->
begin
();
p
!=
orig_params
->
end
();
++
p
)
{
snprintf
(
buf
,
sizeof
buf
,
"pt.%u"
,
count
);
++
count
;
new_params
->
push_back
(
Typed_identifier
(
buf
,
p
->
type
(),
p
->
location
()));
}
}
new_params
->
push_back
(
Typed_identifier
(
"closure.0"
,
closure_type
,
this
->
location_
));
const
Typed_identifier_list
*
orig_results
=
this
->
results_
;
Typed_identifier_list
*
new_results
;
if
(
orig_results
==
NULL
||
orig_results
->
empty
())
new_results
=
NULL
;
else
{
new_results
=
new
Typed_identifier_list
();
for
(
Typed_identifier_list
::
const_iterator
p
=
orig_results
->
begin
();
p
!=
orig_results
->
end
();
++
p
)
new_results
->
push_back
(
Typed_identifier
(
""
,
p
->
type
(),
p
->
location
()));
}
return
Type
::
make_function_type
(
NULL
,
new_params
,
new_results
,
this
->
location
());
}
// Make a function type.
Function_type
*
...
...
@@ -7580,7 +7624,7 @@ Method::bind_method(Expression* expr, Location location) const
// the child class.
return
this
->
do_bind_method
(
expr
,
location
);
}
return
Expression
::
make_bound_method
(
expr
,
this
->
stub_
,
location
);
return
Expression
::
make_bound_method
(
expr
,
this
,
this
->
stub_
,
location
);
}
// Return the named object associated with a method. This may only be
...
...
@@ -7623,8 +7667,8 @@ Expression*
Named_method
::
do_bind_method
(
Expression
*
expr
,
Location
location
)
const
{
Named_object
*
no
=
this
->
named_object_
;
Bound_method_expression
*
bme
=
Expression
::
make_bound_method
(
expr
,
no
,
location
);
Bound_method_expression
*
bme
=
Expression
::
make_bound_method
(
expr
,
this
,
no
,
location
);
// If this is not a local method, and it does not use a stub, then
// the real method expects a different type. We need to cast the
// first argument.
...
...
@@ -9002,28 +9046,16 @@ Type::build_one_stub_method(Gogo* gogo, Method* method,
Call_expression
*
call
=
Expression
::
make_call
(
func
,
arguments
,
is_varargs
,
location
);
call
->
set_hidden_fields_are_ok
();
size_t
count
=
call
->
result_count
();
if
(
count
==
0
)
gogo
->
add_statement
(
Statement
::
make_statement
(
call
,
true
));
else
{
Expression_list
*
retvals
=
new
Expression_list
();
if
(
count
<=
1
)
retvals
->
push_back
(
call
);
else
{
for
(
size_t
i
=
0
;
i
<
count
;
++
i
)
retvals
->
push_back
(
Expression
::
make_call_result
(
call
,
i
));
}
Return_statement
*
retstat
=
Statement
::
make_return_statement
(
retvals
,
location
);
Statement
*
s
=
Statement
::
make_return_from_call
(
call
,
location
);
Return_statement
*
retstat
=
s
->
return_statement
();
if
(
retstat
!=
NULL
)
{
// We can return values with hidden fields from a stub. This is
// necessary if the method is itself hidden.
retstat
->
set_hidden_fields_are_ok
();
gogo
->
add_statement
(
retstat
);
}
gogo
->
add_statement
(
s
);
}
// Apply FIELD_INDEXES to EXPR. The field indexes have to be applied
...
...
gcc/go/gofrontend/types.h
View file @
571d3f91
...
...
@@ -1789,6 +1789,12 @@ class Function_type : public Type
Function_type
*
copy_with_receiver
(
Type
*
)
const
;
// Return a copy of this type ignoring any receiver and adding a
// final closure parameter of type CLOSURE_TYPE. This is used when
// creating descriptors.
Function_type
*
copy_with_closure
(
Type
*
closure_type
)
const
;
static
Type
*
make_function_type_descriptor_type
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment