+2017-12-01 Than McIntosh <thanm@google.com>
+
+ * go-c.h (go_create_gogo_args): Add nil_check_size_threshold
+ field.
+ * go-lang.c (go_langhook_init): Set nil_check_size_threshold.
+
2017-11-28 Jakub Jelinek <jakub@redhat.com>
* go-gcc.cc (Gcc_backend::switch_statement): Build SWITCH_EXPR using
bool check_divide_overflow;
bool compiling_runtime;
int debug_escape_level;
+ int64_t nil_check_size_threshold;
};
extern void go_create_gogo (const struct go_create_gogo_args*);
args.check_divide_overflow = go_check_divide_overflow;
args.compiling_runtime = go_compiling_runtime;
args.debug_escape_level = go_debug_escape_level;
+ args.nil_check_size_threshold = 4096;
args.linemap = go_get_linemap();
args.backend = go_get_backend();
go_create_gogo (&args);
-0d6b3abcbfe04949db947081651a503ceb12fe6e
+8cd42a3e9e0e618bb09e67be73f7d2f2477a0faa
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
Expression::make_interface_info(rhs, INTERFACE_INFO_METHODS, location);
Expression* descriptor =
- Expression::make_unary(OPERATOR_MULT, mtable, location);
+ Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, location);
descriptor = Expression::make_field_reference(descriptor, 0, location);
Expression* nil = Expression::make_nil(location);
{
obj = Expression::make_unsafe_cast(Type::make_pointer_type(lhs_type), obj,
location);
- obj = Expression::make_unary(OPERATOR_MULT, obj, location);
+ obj = Expression::make_dereference(obj, NIL_CHECK_NOT_NEEDED,
+ location);
}
return Expression::make_compound(check_iface, obj, location);
}
&& !this->expr_->is_variable())
{
go_assert(this->expr_->type()->points_to() != NULL);
- Type* ptype = this->expr_->type()->points_to();
- if (!ptype->is_void_type())
+ switch (this->requires_nil_check(gogo))
{
- int64_t s;
- bool ok = ptype->backend_type_size(gogo, &s);
- if (!ok)
+ case NIL_CHECK_ERROR_ENCOUNTERED:
{
go_assert(saw_errors());
return Expression::make_error(this->location());
}
- if (s >= 4096 || this->issue_nil_check_)
- {
- Temporary_statement* temp =
- Statement::make_temporary(NULL, this->expr_, location);
- inserter->insert(temp);
- this->expr_ =
- Expression::make_temporary_reference(temp, location);
- }
+ case NIL_CHECK_NOT_NEEDED:
+ break;
+ case NIL_CHECK_NEEDED:
+ this->create_temp_ = true;
+ break;
+ case NIL_CHECK_DEFAULT:
+ go_unreachable();
}
}
return false;
}
+// Return whether this dereference expression requires an explicit nil
+// check. If we are dereferencing the pointer to a large struct
+// (greater than the specified size threshold), we need to check for
+// nil. We don't bother to check for small structs because we expect
+// the system to crash on a nil pointer dereference. However, if we
+// know the address of this expression is being taken, we must always
+// check for nil.
+Unary_expression::Nil_check_classification
+Unary_expression::requires_nil_check(Gogo* gogo)
+{
+ go_assert(this->op_ == OPERATOR_MULT);
+ go_assert(this->expr_->type()->points_to() != NULL);
+
+ if (this->issue_nil_check_ == NIL_CHECK_NEEDED)
+ return NIL_CHECK_NEEDED;
+ else if (this->issue_nil_check_ == NIL_CHECK_NOT_NEEDED)
+ return NIL_CHECK_NOT_NEEDED;
+
+ Type* ptype = this->expr_->type()->points_to();
+ int64_t type_size = -1;
+ if (!ptype->is_void_type())
+ {
+ bool ok = ptype->backend_type_size(gogo, &type_size);
+ if (!ok)
+ return NIL_CHECK_ERROR_ENCOUNTERED;
+ }
+
+ int64_t size_cutoff = gogo->nil_check_size_threshold();
+ if (size_cutoff == -1 || (type_size != -1 && type_size >= size_cutoff))
+ this->issue_nil_check_ = NIL_CHECK_NEEDED;
+ else
+ this->issue_nil_check_ = NIL_CHECK_NOT_NEEDED;
+ return this->issue_nil_check_;
+}
+
// Apply unary opcode OP to UNC, setting NC. Return true if this
// could be done, false if not. On overflow, issues an error and sets
// *ISSUED_ERROR.
{
go_assert(this->expr_->type()->points_to() != NULL);
- // If we are dereferencing the pointer to a large struct, we
- // need to check for nil. We don't bother to check for small
- // structs because we expect the system to crash on a nil
- // pointer dereference. However, if we know the address of this
- // expression is being taken, we must always check for nil.
-
+ bool known_valid = false;
Type* ptype = this->expr_->type()->points_to();
Btype* pbtype = ptype->get_backend(gogo);
- if (!ptype->is_void_type())
- {
- int64_t s;
- bool ok = ptype->backend_type_size(gogo, &s);
- if (!ok)
+ switch (this->requires_nil_check(gogo))
+ {
+ case NIL_CHECK_NOT_NEEDED:
+ break;
+ case NIL_CHECK_ERROR_ENCOUNTERED:
{
go_assert(saw_errors());
return gogo->backend()->error_expression();
}
- if (s >= 4096 || this->issue_nil_check_)
- {
+ case NIL_CHECK_NEEDED:
+ {
go_assert(this->expr_->is_variable());
Bexpression* nil =
- Expression::make_nil(loc)->get_backend(context);
+ Expression::make_nil(loc)->get_backend(context);
Bexpression* compare =
gogo->backend()->binary_expression(OPERATOR_EQEQ, bexpr,
nil, loc);
Bexpression* crash =
- gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
- loc)->get_backend(context);
+ gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
+ loc)->get_backend(context);
Bfunction* bfn = context->function()->func_value()->get_decl();
bexpr = gogo->backend()->conditional_expression(bfn, btype,
compare,
crash, bexpr,
loc);
-
- }
- }
- ret = gogo->backend()->indirect_expression(pbtype, bexpr, false, loc);
+ known_valid = true;
+ break;
+ }
+ case NIL_CHECK_DEFAULT:
+ go_unreachable();
+ }
+ ret = gogo->backend()->indirect_expression(pbtype, bexpr,
+ known_valid, loc);
}
break;
return new Unary_expression(op, expr, location);
}
+Expression*
+Expression::make_dereference(Expression* ptr,
+ Nil_check_classification docheck,
+ Location location)
+{
+ Expression* deref = Expression::make_unary(OPERATOR_MULT, ptr, location);
+ if (docheck == NIL_CHECK_NEEDED)
+ deref->unary_expression()->set_requires_nil_check(true);
+ else if (docheck == NIL_CHECK_NOT_NEEDED)
+ deref->unary_expression()->set_requires_nil_check(false);
+ return deref;
+}
+
// If this is an indirection through a pointer, return the expression
// being pointed through. Otherwise return this.
// 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_dereference(arg, NIL_CHECK_NOT_NEEDED, loc);
arg = Expression::make_field_reference(arg, 1, loc);
Expression* bme = Expression::make_bound_method(arg, method, fn, loc);
Expression::make_nil(loc),
loc);
cond = Expression::make_binary(OPERATOR_OROR, cond, n, loc);
- *ref = Expression::make_unary(OPERATOR_MULT, *ref, loc);
+ *ref = Expression::make_dereference(*ref, Expression::NIL_CHECK_DEFAULT,
+ loc);
go_assert((*ref)->type()->struct_type() == stype);
}
*ref = Expression::make_field_reference(*ref, field_indexes->field_index,
Expression* val = expr;
if (fntype->receiver()->type()->points_to() == NULL
&& val->type()->points_to() != NULL)
- val = Expression::make_unary(OPERATOR_MULT, val, loc);
+ val = Expression::make_dereference(val, NIL_CHECK_DEFAULT, 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
arg_type = arg_type->points_to();
go_assert(arg_type->array_type() != NULL
&& !arg_type->is_slice_type());
- arg = Expression::make_unary(OPERATOR_MULT, arg, location);
+ arg = Expression::make_dereference(arg, NIL_CHECK_DEFAULT,
+ location);
}
Type* int_type = Type::lookup_integer_type("int");
arg, nil, location);
Expression* zero = Expression::make_integer_ul(0, int_type,
location);
- Expression* indir = Expression::make_unary(OPERATOR_MULT,
- arg, location);
+ Expression* indir =
+ Expression::make_dereference(arg, NIL_CHECK_NOT_NEEDED,
+ location);
val = Expression::make_conditional(cmp, zero, indir, location);
}
else
arg, nil, location);
Expression* zero = Expression::make_integer_ul(0, int_type,
location);
- Expression* indir = Expression::make_unary(OPERATOR_MULT,
- parg, location);
+ Expression* indir =
+ Expression::make_dereference(parg, NIL_CHECK_NOT_NEEDED,
+ location);
val = Expression::make_conditional(cmp, zero, indir, location);
}
else
Type::make_pointer_type(
Type::make_pointer_type(Type::make_void_type()));
fn = Expression::make_unsafe_cast(pfntype, this->fn_, location);
- fn = Expression::make_unary(OPERATOR_MULT, fn, location);
+ fn = Expression::make_dereference(fn, NIL_CHECK_NOT_NEEDED, location);
}
else
{
&& type->points_to()->array_type() != NULL
&& !type->points_to()->is_slice_type())
{
- Expression* deref = Expression::make_unary(OPERATOR_MULT, left,
- location);
+ Expression* deref =
+ Expression::make_dereference(left, NIL_CHECK_DEFAULT, location);
// For an ordinary index into the array, the pointer will be
// dereferenced. For a slice it will not--the resulting slice
Location loc = this->location();
Expression* string_arg = this->string_;
if (this->string_->type()->points_to() != NULL)
- string_arg = Expression::make_unary(OPERATOR_MULT, this->string_, loc);
+ string_arg = Expression::make_dereference(this->string_,
+ NIL_CHECK_NOT_NEEDED, loc);
Expression* bad_index = Expression::check_bounds(this->start_, loc);
go_assert(this->value_pointer_ != NULL
&& this->value_pointer_->is_variable());
- Expression* val = Expression::make_unary(OPERATOR_MULT, this->value_pointer_,
- this->location());
+ Expression* val = Expression::make_dereference(this->value_pointer_,
+ NIL_CHECK_NOT_NEEDED,
+ this->location());
return val->get_backend(context);
}
Expression* ref = this->expr_;
Location loc = this->location();
if (ref->type()->points_to() != NULL)
- ref = Expression::make_unary(OPERATOR_MULT, ref, loc);
+ ref = Expression::make_dereference(ref, NIL_CHECK_DEFAULT, loc);
Expression* mtable =
Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc);
unsigned int index;
const Struct_field* field = mtable_type->find_local_field(name, &index);
go_assert(field != NULL);
- mtable = Expression::make_unary(OPERATOR_MULT, mtable, loc);
+
+ mtable = Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, loc);
return Expression::make_field_reference(mtable, index, loc);
}
{
Expression* expr = this->expr_;
if (expr->type()->points_to() != NULL)
- expr = Expression::make_unary(OPERATOR_MULT, expr, this->location());
+ expr = Expression::make_dereference(expr, NIL_CHECK_DEFAULT,
+ this->location());
return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT,
this->location());
}
// 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_dereference(arg, NIL_CHECK_NOT_NEEDED, loc);
arg = Expression::make_field_reference(arg, 1, loc);
Expression *ifre = Expression::make_interface_field_reference(arg, name,
static Expression*
make_backend(Bexpression*, Type*, Location);
+ enum Nil_check_classification
+ {
+ // Use the default policy for deciding if this deref needs a check.
+ NIL_CHECK_DEFAULT,
+ // An explicit check is required for this dereference operation.
+ NIL_CHECK_NEEDED,
+ // No check needed for this dereference operation.
+ NIL_CHECK_NOT_NEEDED,
+ // A type error or error construct was encountered when determining
+ // whether this deref needs an explicit check.
+ NIL_CHECK_ERROR_ENCOUNTERED
+ };
+
+ // Make a dereference expression.
+ static Expression*
+ make_dereference(Expression*, Nil_check_classification, Location);
+
// Return the expression classification.
Expression_classification
classification() const
Unary_expression(Operator op, Expression* expr, Location location)
: Expression(EXPRESSION_UNARY, location),
op_(op), escapes_(true), create_temp_(false), is_gc_root_(false),
- is_slice_init_(false), expr_(expr), issue_nil_check_(false)
+ is_slice_init_(false), expr_(expr),
+ issue_nil_check_(NIL_CHECK_DEFAULT)
{ }
// Return the operator.
static Expression*
do_import(Import*);
+ // Declare that this deref does or does not require an explicit nil check.
+ void
+ set_requires_nil_check(bool needed)
+ {
+ go_assert(this->op_ == OPERATOR_MULT);
+ if (needed)
+ this->issue_nil_check_ = NIL_CHECK_NEEDED;
+ else
+ this->issue_nil_check_ = NIL_CHECK_NOT_NEEDED;
+ }
+
protected:
int
do_traverse(Traverse* traverse)
void
do_issue_nil_check()
- { this->issue_nil_check_ = (this->op_ == OPERATOR_MULT); }
+ {
+ if (this->op_ == OPERATOR_MULT)
+ this->set_requires_nil_check(true);
+ }
private:
static bool
base_is_static_initializer(Expression*);
+ // Return a determination as to whether this dereference expression
+ // requires a nil check.
+ Nil_check_classification
+ requires_nil_check(Gogo*);
+
// The unary operator to apply.
Operator op_;
// Normally true. False if this is an address expression which does
Expression* expr_;
// Whether or not to issue a nil check for this expression if its address
// is being taken.
- bool issue_nil_check_;
+ Nil_check_classification issue_nil_check_;
};
// A binary expression.
if (args->c_header != NULL)
::gogo->set_c_header(args->c_header);
::gogo->set_debug_escape_level(args->debug_escape_level);
+ ::gogo->set_nil_check_size_threshold(args->nil_check_size_threshold);
}
// Parse the input files.
check_divide_overflow_(true),
compiling_runtime_(false),
debug_escape_level_(0),
+ nil_check_size_threshold_(4096),
verify_types_(),
interface_types_(),
specific_type_functions_(),
vars.push_back(bvar);
Expression* parm_ref =
Expression::make_var_reference(parm_no, loc);
- parm_ref = Expression::make_unary(OPERATOR_MULT, parm_ref, loc);
+ parm_ref =
+ Expression::make_dereference(parm_ref,
+ Expression::NIL_CHECK_DEFAULT,
+ loc);
if ((*p)->var_value()->is_in_heap())
parm_ref = Expression::make_heap_expression(parm_ref, loc);
var_inits.push_back(parm_ref->get_backend(&context));
set_debug_escape_level(int level)
{ this->debug_escape_level_ = level; }
+ // Return the size threshold used to determine whether to issue
+ // a nil-check for a given pointer dereference. A threshold of -1
+ // implies that all potentially faulting dereference ops should
+ // be nil-checked. A positive threshold of N implies that a deref
+ // of *P where P has size less than N doesn't need a nil check.
+ int64_t
+ nil_check_size_threshold() const
+ { return this->nil_check_size_threshold_; }
+
+ // Set the nil-check size threshold, as described above.
+ void
+ set_nil_check_size_threshold(int64_t bytes)
+ { this->nil_check_size_threshold_ = bytes; }
+
// Import a package. FILENAME is the file name argument, LOCAL_NAME
// is the local name to give to the package. If LOCAL_NAME is empty
// the declarations are added to the global scope.
// The level of escape analysis debug information to emit, from the
// -fgo-debug-escape option.
int debug_escape_level_;
+ // Nil-check size threshhold.
+ int64_t nil_check_size_threshold_;
// A list of types to verify.
std::vector<Type*> verify_types_;
// A list of interface types defined while parsing.
Expression* closure_ref = Expression::make_var_reference(closure,
location);
- closure_ref = Expression::make_unary(OPERATOR_MULT, closure_ref, location);
+ closure_ref =
+ Expression::make_dereference(closure_ref,
+ Expression::NIL_CHECK_DEFAULT,
+ location);
// The closure structure holds pointers to the variables, so we need
// to introduce an indirection.
Expression* e = Expression::make_field_reference(closure_ref,
ins.first->index(),
location);
- e = Expression::make_unary(OPERATOR_MULT, e, location);
+ e = Expression::make_dereference(e, Expression::NIL_CHECK_DEFAULT, location);
return Expression::make_enclosing_var_reference(e, var, location);
}
if (binit != NULL)
{
Expression* e = Expression::make_temporary_reference(temp, loc);
- e = Expression::make_unary(OPERATOR_MULT, e, loc);
+ e = Expression::make_dereference(e, Expression::NIL_CHECK_NOT_NEEDED,
+ loc);
Bexpression* be = e->get_backend(context);
set = context->backend()->assignment_statement(bfunction, be, binit, loc);
}
a1, a2, a3);
Type* ptrval_type = Type::make_pointer_type(mt->val_type());
call = Expression::make_cast(ptrval_type, call, loc);
- Expression* indir = Expression::make_unary(OPERATOR_MULT, call, loc);
+ Expression* indir =
+ Expression::make_dereference(call, Expression::NIL_CHECK_NOT_NEEDED,
+ loc);
ref = Expression::make_temporary_reference(val_temp, loc);
b->add_statement(Statement::make_assignment(indir, ref, loc));
// val = *val__ptr_temp
ref = Expression::make_temporary_reference(val_ptr_temp, loc);
- Expression* ind = Expression::make_unary(OPERATOR_MULT, ref, loc);
+ Expression* ind =
+ Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED, loc);
s = Statement::make_assignment(this->val_, ind, loc);
b->add_statement(s);
// ones used in build_struct.
Expression* thunk_parameter = Expression::make_var_reference(named_parameter,
location);
- thunk_parameter = Expression::make_unary(OPERATOR_MULT, thunk_parameter,
- location);
+ thunk_parameter =
+ Expression::make_dereference(thunk_parameter,
+ Expression::NIL_CHECK_NOT_NEEDED,
+ location);
Interface_field_reference_expression* interface_method =
ce->fn()->interface_field_reference_expression();
{
Expression* thunk_param =
Expression::make_var_reference(named_parameter, location);
- thunk_param =
- Expression::make_unary(OPERATOR_MULT, thunk_param, location);
+ thunk_param =
+ Expression::make_dereference(thunk_param,
+ Expression::NIL_CHECK_NOT_NEEDED,
+ location);
param = Expression::make_field_reference(thunk_param,
next_index,
location);
Expression* lhs = Expression::make_temporary_reference(index_temp, loc);
Expression* rhs = Expression::make_temporary_reference(hiter, loc);
rhs = Expression::make_field_reference(ref, 0, loc);
- rhs = Expression::make_unary(OPERATOR_MULT, ref, loc);
+ rhs = Expression::make_dereference(ref, Expression::NIL_CHECK_NOT_NEEDED,
+ loc);
Statement* set = Statement::make_assignment(lhs, rhs, loc);
iter_init->add_statement(set);
lhs = Expression::make_temporary_reference(value_temp, loc);
rhs = Expression::make_temporary_reference(hiter, loc);
rhs = Expression::make_field_reference(rhs, 1, loc);
- rhs = Expression::make_unary(OPERATOR_MULT, rhs, loc);
+ rhs = Expression::make_dereference(rhs, Expression::NIL_CHECK_NOT_NEEDED,
+ loc);
set = Statement::make_assignment(lhs, rhs, loc);
iter_init->add_statement(set);
}
// Compare the values for equality.
Expression* t1 = Expression::make_temporary_reference(p1, bloc);
- t1 = Expression::make_unary(OPERATOR_MULT, t1, bloc);
+ t1 = Expression::make_dereference(t1, Expression::NIL_CHECK_NOT_NEEDED, bloc);
Expression* t2 = Expression::make_temporary_reference(p2, bloc);
- t2 = Expression::make_unary(OPERATOR_MULT, t2, bloc);
+ t2 = Expression::make_dereference(t2, Expression::NIL_CHECK_NOT_NEEDED, bloc);
Expression* cond = Expression::make_binary(OPERATOR_EQEQ, t1, t2, bloc);
Expression* here = Expression::make_field_reference(struct_expr, i,
location);
if (pf->type()->points_to() != NULL)
- here = Expression::make_unary(OPERATOR_MULT, here, location);
+ here = Expression::make_dereference(here,
+ Expression::NIL_CHECK_DEFAULT,
+ location);
while (sub->expr() != NULL)
{
sub = sub->expr()->deref()->field_reference_expression();
// Compare one field in both P1 and P2.
Expression* f1 = Expression::make_temporary_reference(p1, bloc);
- f1 = Expression::make_unary(OPERATOR_MULT, f1, bloc);
+ f1 = Expression::make_dereference(f1, Expression::NIL_CHECK_DEFAULT,
+ bloc);
f1 = Expression::make_field_reference(f1, field_index, bloc);
Expression* f2 = Expression::make_temporary_reference(p2, bloc);
- f2 = Expression::make_unary(OPERATOR_MULT, f2, bloc);
+ f2 = Expression::make_dereference(f2, Expression::NIL_CHECK_DEFAULT,
+ bloc);
f2 = Expression::make_field_reference(f2, field_index, bloc);
Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, f1, f2, bloc);
// Compare element in P1 and P2.
Expression* e1 = Expression::make_temporary_reference(p1, bloc);
- e1 = Expression::make_unary(OPERATOR_MULT, e1, bloc);
+ e1 = Expression::make_dereference(e1, Expression::NIL_CHECK_DEFAULT, bloc);
ref = Expression::make_temporary_reference(index, bloc);
e1 = Expression::make_array_index(e1, ref, NULL, NULL, bloc);
Expression* e2 = Expression::make_temporary_reference(p2, bloc);
- e2 = Expression::make_unary(OPERATOR_MULT, e2, bloc);
+ e2 = Expression::make_dereference(e2, Expression::NIL_CHECK_DEFAULT, bloc);
ref = Expression::make_temporary_reference(index, bloc);
e2 = Expression::make_array_index(e2, ref, NULL, NULL, bloc);
if (expr->type()->struct_type() == NULL)
{
go_assert(expr->type()->points_to() != NULL);
- expr = Expression::make_unary(OPERATOR_MULT, expr, location);
+ expr = Expression::make_dereference(expr, Expression::NIL_CHECK_DEFAULT,
+ location);
go_assert(expr->type()->struct_type() == stype);
}
return Expression::make_field_reference(expr, field_indexes->field_index,
&& type->points_to() != NULL
&& type->points_to()->points_to() != NULL)
{
- expr = Expression::make_unary(OPERATOR_MULT, expr, location);
+ expr = Expression::make_dereference(expr, Expression::NIL_CHECK_DEFAULT,
+ location);
type = type->points_to();
if (type->deref()->is_error_type())
return Expression::make_error(location);
return Expression::make_error(location);
}
go_assert(type->points_to() != NULL);
- expr = Expression::make_unary(OPERATOR_MULT, expr,
- location);
+ expr = Expression::make_dereference(expr,
+ Expression::NIL_CHECK_DEFAULT,
+ location);
go_assert(expr->type()->struct_type() == st);
}
ret = st->field_reference(expr, name, location);
rhs = Expression::make_temporary_reference(rhs_temp, loc);
}
- Expression* indir = Expression::make_unary(OPERATOR_MULT, lhs, loc);
+ Expression* indir =
+ Expression::make_dereference(lhs, Expression::NIL_CHECK_DEFAULT, loc);
Statement* assign = Statement::make_assignment(indir, rhs, loc);
lhs = Expression::make_temporary_reference(lhs_temp, loc);