auto merge of #9538 : thestinger/rust/type_use, r=pcwalton

This is broken, and results in poor performance due to the undefined
behaviour in the LLVM IR. LLVM's `mergefunc` is a *much* better way of
doing this since it merges based on the equality of the bytecode.

For example, consider `std::repr`. It generates different code per
type, but is not included in the type bounds of generics.

The `mergefunc` pass works for most of our code but currently hits an
assert on libstd. It is receiving attention upstream so it will be
ready soon, but I don't think removing this broken code should wait any
longer. I've opened #9536 about enabling it by default.

Closes #8651
Closes #3547
Closes #2537
Closes #6971
Closes #9222
This commit is contained in:
bors 2013-09-27 03:31:13 -07:00
commit ae8a2ff379
9 changed files with 21 additions and 626 deletions

View File

@ -1,79 +0,0 @@
#!/usr/bin/perl
#
# This is a tool that helps with debugging incorrect monomorphic instance collapse.
#
# To use:
# $ RUST_LOG=rustc::middle::trans::monomorphize rustc ARGS 2>&1 >log.txt
# $ ./monodebug.pl log.txt
#
# This will show all generics that got collapsed. You can inspect this list to find the instances
# that were mistakenly combined into one. Fixes will (most likely) be applied to type_use.rs.
#
# Questions about this tool go to pcwalton.
#
use strict;
use warnings;
use Data::Dumper qw(Dumper);
use Text::Balanced qw(extract_bracketed);
my %funcs;
while (<>) {
chomp;
/^rust: ~"monomorphic_fn\((.*)"$/ or next;
my $text = $1;
$text =~ /fn_id=(\{ crate: \d+, node: \d+ \} \([^)]+\)), real_substs=(.*?), substs=(.*?), hash_id = \@\{ (.*) \}$/ or next;
my ($fn_id, $real_substs, $substs, $hash_id) = ($1, $2, $3, $4);
#print "$hash_id\n";
$hash_id =~ /^def: { crate: \d+, node: \d+ }, params: ~\[ (.*) \], impl_did_opt: (?:None|Some\({ crate: \d+, node: \d+ }\))$/ or next;
my $params = $1;
my @real_substs;
@real_substs = $real_substs =~ /\\"(.*?)\\"/g;
my @mono_params;
while (1) {
$params =~ s/^, //;
if ($params =~ s/^mono_precise//) {
extract_bracketed($params, '()');
push @mono_params, 'precise';
next;
}
if ($params =~ s/^mono_repr//) {
my $sub = extract_bracketed($params, '()');
push @mono_params, "repr($sub)";
next;
}
if ($params =~ s/^mono_any//) {
push @mono_params, "any";
next;
}
last;
}
my @key_params;
for (my $i = 0; $i < @mono_params; ++$i) {
if ($mono_params[$i] eq 'precise') {
push @key_params, 'precise(' . $real_substs[$i] . ')';
} else {
push @key_params, $mono_params[$i];
}
}
my $key = "$fn_id with " . (join ', ', @key_params);
$funcs{$key}{$real_substs} = 1;
}
while (my ($key, $substs) = each %funcs) {
my @params = keys %$substs;
next if @params == 1;
print "$key\n";
print(('-' x (length $key)), $/);
for my $param (@params) {
print "$param\n";
}
print "\n";
}

View File

@ -71,7 +71,6 @@ _rustc_opts_debug=(
'count-type-sizes:count the sizes of aggregate types'
'meta-stats:gather metadata statistics'
'no-opt:do not optimize, even if -O is passed'
'no-monomorphic-collapse:do not collapse template instantiations'
'print-link-args:Print the arguments passed to the linker'
'gc:Garbage collect shared data (experimental)'
'jit:Execute using JIT (experimental)'

View File

@ -748,13 +748,6 @@ pub fn build_session_options(binary: @str,
let debuginfo = debugging_opts & session::debug_info != 0 ||
extra_debuginfo;
// If debugging info is generated, do not collapse monomorphized function instances.
// Functions with equivalent llvm code still need separate debugging descriptions because names
// might differ.
if debuginfo {
debugging_opts |= session::no_monomorphic_collapse;
}
let statik = debugging_opts & session::statik != 0;
let addl_lib_search_paths = matches.opt_strs("L").map(|s| Path(*s));

View File

@ -67,20 +67,19 @@ pub static debug_llvm: uint = 1 << 13;
pub static count_type_sizes: uint = 1 << 14;
pub static meta_stats: uint = 1 << 15;
pub static no_opt: uint = 1 << 16;
pub static no_monomorphic_collapse: uint = 1 << 17;
pub static gc: uint = 1 << 18;
pub static jit: uint = 1 << 19;
pub static debug_info: uint = 1 << 20;
pub static extra_debug_info: uint = 1 << 21;
pub static statik: uint = 1 << 22;
pub static print_link_args: uint = 1 << 23;
pub static no_debug_borrows: uint = 1 << 24;
pub static lint_llvm: uint = 1 << 25;
pub static once_fns: uint = 1 << 26;
pub static print_llvm_passes: uint = 1 << 27;
pub static no_vectorize_loops: uint = 1 << 28;
pub static no_vectorize_slp: uint = 1 << 29;
pub static no_prepopulate_passes: uint = 1 << 30;
pub static gc: uint = 1 << 17;
pub static jit: uint = 1 << 18;
pub static debug_info: uint = 1 << 19;
pub static extra_debug_info: uint = 1 << 20;
pub static statik: uint = 1 << 21;
pub static print_link_args: uint = 1 << 22;
pub static no_debug_borrows: uint = 1 << 23;
pub static lint_llvm: uint = 1 << 24;
pub static once_fns: uint = 1 << 25;
pub static print_llvm_passes: uint = 1 << 26;
pub static no_vectorize_loops: uint = 1 << 27;
pub static no_vectorize_slp: uint = 1 << 28;
pub static no_prepopulate_passes: uint = 1 << 29;
pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
~[(~"verbose", ~"in general, enable more debug printouts", verbose),
@ -106,8 +105,6 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
count_type_sizes),
(~"meta-stats", ~"gather metadata statistics", meta_stats),
(~"no-opt", ~"do not optimize, even if -O is passed", no_opt),
(~"no-monomorphic-collapse", ~"do not collapse template instantiations",
no_monomorphic_collapse),
(~"print-link-args", ~"Print the arguments passed to the linker", print_link_args),
(~"gc", ~"Garbage collect shared data (experimental)", gc),
(~"jit", ~"Execute using JIT (experimental)", jit),
@ -326,9 +323,6 @@ impl Session_ {
pub fn borrowck_note_loan(&self) -> bool {
self.debugging_opt(borrowck_note_loan)
}
pub fn no_monomorphic_collapse(&self) -> bool {
self.debugging_opt(no_monomorphic_collapse)
}
pub fn debug_borrows(&self) -> bool {
self.opts.optimize == No && !self.debugging_opt(no_debug_borrows)
}

View File

@ -22,7 +22,6 @@ use middle::trans::adt;
use middle::trans::base;
use middle::trans::builder::Builder;
use middle::trans::debuginfo;
use middle::trans::type_use;
use middle::trans::common::{C_i32, C_null};
use middle::ty;
@ -73,8 +72,6 @@ pub struct CrateContext {
// Cache instances of monomorphized functions
monomorphized: HashMap<mono_id, ValueRef>,
monomorphizing: HashMap<ast::DefId, uint>,
// Cache computed type parameter uses (see type_use.rs)
type_use_cache: HashMap<ast::DefId, @~[type_use::type_uses]>,
// Cache generated vtables
vtables: HashMap<(ty::t, mono_id), ValueRef>,
// Cache of constant strings,
@ -204,7 +201,6 @@ impl CrateContext {
non_inlineable_statics: HashSet::new(),
monomorphized: HashMap::new(),
monomorphizing: HashMap::new(),
type_use_cache: HashMap::new(),
vtables: HashMap::new(),
const_cstr_cache: HashMap::new(),
const_globals: HashMap::new(),

View File

@ -520,8 +520,7 @@ pub fn vtable_id(ccx: @mut CrateContext,
monomorphize::make_mono_id(
ccx,
impl_id,
&psubsts,
None)
&psubsts)
}
// can't this be checked at the callee?

View File

@ -38,7 +38,6 @@ pub mod foreign;
pub mod intrinsic;
pub mod reflect;
pub mod debuginfo;
pub mod type_use;
pub mod machine;
pub mod adt;
pub mod asm;

View File

@ -18,15 +18,11 @@ use middle::trans::base::{trans_fn, decl_internal_rust_fn};
use middle::trans::base::{get_item_val, no_self};
use middle::trans::base;
use middle::trans::common::*;
use middle::trans::datum;
use middle::trans::machine;
use middle::trans::meth;
use middle::trans::type_of;
use middle::trans::type_use;
use middle::trans::intrinsic;
use middle::ty;
use middle::typeck;
use util::ppaux::{Repr,ty_to_str};
use util::ppaux::Repr;
use syntax::ast;
use syntax::ast_map;
@ -65,10 +61,8 @@ pub fn monomorphic_fn(ccx: @mut CrateContext,
for s in real_substs.tps.iter() { assert!(!ty::type_has_params(*s)); }
for s in psubsts.tys.iter() { assert!(!ty::type_has_params(*s)); }
let param_uses = type_use::type_uses_for(ccx, fn_id, psubsts.tys.len());
let hash_id = make_mono_id(ccx, fn_id, &*psubsts, Some(param_uses));
let hash_id = make_mono_id(ccx, fn_id, &*psubsts);
if hash_id.params.iter().any(
|p| match *p { mono_precise(_, _) => false, _ => true }) {
must_cast = true;
@ -302,8 +296,7 @@ pub fn monomorphic_fn(ccx: @mut CrateContext,
pub fn make_mono_id(ccx: @mut CrateContext,
item: ast::DefId,
substs: &param_substs,
param_uses: Option<@~[type_use::type_uses]>) -> mono_id {
substs: &param_substs) -> mono_id {
// FIXME (possibly #5801): Need a lot of type hints to get
// .collect() to work.
let substs_iter = substs.self_ty.iter().chain(substs.tys.iter());
@ -321,59 +314,9 @@ pub fn make_mono_id(ccx: @mut CrateContext,
};
let param_ids = match param_uses {
Some(ref uses) => {
// param_uses doesn't include a use for the self type.
// We just say it is fully used.
let self_use =
substs.self_ty.map(|_| type_use::use_repr|type_use::use_tydesc);
let uses_iter = self_use.iter().chain(uses.iter());
precise_param_ids.iter().zip(uses_iter).map(|(id, uses)| {
if ccx.sess.no_monomorphic_collapse() {
match *id {
(a, b) => mono_precise(a, b)
}
} else {
match *id {
(a, b@Some(_)) => mono_precise(a, b),
(subst, None) => {
if *uses == 0 {
mono_any
} else if *uses == type_use::use_repr &&
!ty::type_needs_drop(ccx.tcx, subst)
{
let llty = type_of::type_of(ccx, subst);
let size = machine::llbitsize_of_real(ccx, llty);
let align = machine::llalign_of_min(ccx, llty);
let mode = datum::appropriate_mode(ccx.tcx, subst);
let data_class = mono_data_classify(subst);
debug!("make_mono_id: type %s -> size %u align %u mode %? class %?",
ty_to_str(ccx.tcx, subst),
size, align, mode, data_class);
// Special value for nil to prevent problems
// with undef return pointers.
if size <= 8u && ty::type_is_nil(subst) {
mono_repr(0u, 0u, data_class, mode)
} else {
mono_repr(size, align, data_class, mode)
}
} else {
mono_precise(subst, None)
}
}
}
}
}).collect()
}
None => {
precise_param_ids.iter().map(|x| {
let (a, b) = *x;
mono_precise(a, b)
}).collect()
}
};
let param_ids = precise_param_ids.iter().map(|x| {
let (a, b) = *x;
mono_precise(a, b)
}).collect();
@mono_id_ {def: item, params: param_ids}
}

View File

@ -1,449 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Determines the ways in which a generic function body depends
// on its type parameters. Used to aggressively reuse compiled
// function bodies for different types.
// This unfortunately depends on quite a bit of knowledge about the
// details of the language semantics, and is likely to accidentally go
// out of sync when something is changed. It could be made more
// powerful by distinguishing between functions that only need to know
// the size and alignment of a type, and those that also use its
// drop/take glue. But this would increase the fragility of the code
// to a ridiculous level, and probably not catch all that many extra
// opportunities for reuse.
// (An other approach to doing what this code does is to instrument
// the translation code to set flags whenever it does something like
// alloca a type or get a tydesc. This would not duplicate quite as
// much information, but have the disadvantage of being very
// invasive.)
use middle::freevars;
use middle::trans::common::*;
use middle::trans::inline;
use middle::ty;
use middle::typeck;
use std::option::{Some, None};
use std::vec;
use extra::list::{List, Cons, Nil};
use extra::list;
use syntax::ast;
use syntax::ast::*;
use syntax::ast_map;
use syntax::ast_util;
use syntax::parse::token;
use syntax::visit;
use syntax::visit::Visitor;
pub type type_uses = uint; // Bitmask
pub static use_repr: uint = 1; /* Dependency on size/alignment/mode and
take/drop glue */
pub static use_tydesc: uint = 2; /* Takes the tydesc, or compares */
pub static use_all: uint = use_repr|use_tydesc;
#[deriving(Clone)]
pub struct Context {
ccx: @mut CrateContext,
uses: @mut ~[type_uses]
}
pub fn type_uses_for(ccx: @mut CrateContext, fn_id: DefId, n_tps: uint)
-> @~[type_uses] {
fn store_type_uses(cx: Context, fn_id: DefId) -> @~[type_uses] {
let Context { uses, ccx } = cx;
let uses = @(*uses).clone(); // freeze
ccx.type_use_cache.insert(fn_id, uses);
uses
}
match ccx.type_use_cache.find(&fn_id) {
Some(uses) => return *uses,
None => ()
}
let fn_id_loc = if fn_id.crate == LOCAL_CRATE {
fn_id
} else {
inline::maybe_instantiate_inline(ccx, fn_id)
};
// Conservatively assume full use for recursive loops
ccx.type_use_cache.insert(fn_id, @vec::from_elem(n_tps, use_all));
let mut cx = Context {
ccx: ccx,
uses: @mut vec::from_elem(n_tps, 0u)
};
// If the method is a default method, we mark all of the types as
// used. This is imprecise, but simple. Getting it right is
// tricky because the substs on the call and the substs on the
// default method differ, because of substs on the trait/impl.
let is_default = ty::provided_source(ccx.tcx, fn_id_loc).is_some();
// We also mark all of the params as used if it is an extern thing
// that we haven't been able to inline yet.
if is_default || fn_id_loc.crate != LOCAL_CRATE {
for n in range(0u, n_tps) { cx.uses[n] |= use_all; }
return store_type_uses(cx, fn_id);
}
let map_node = match ccx.tcx.items.find(&fn_id_loc.node) {
Some(x) => {
(*x).clone()
}
None => {
ccx.sess.bug(fmt!("type_uses_for: unbound item ID %?",
fn_id_loc))
}
};
match map_node {
ast_map::node_item(@ast::item { node: item_fn(_, _, _, _, ref body),
_ }, _) |
ast_map::node_method(@ast::method {body: ref body, _}, _, _) => {
handle_body(&mut cx, body);
}
ast_map::node_trait_method(*) => {
// This will be a static trait method. For now, we just assume
// it fully depends on all of the type information. (Doing
// otherwise would require finding the actual implementation).
for n in range(0u, n_tps) { cx.uses[n] |= use_all;}
// We need to return early, before the arguments are processed,
// because of difficulties in the handling of Self.
return store_type_uses(cx, fn_id);
}
ast_map::node_variant(_, _, _) => {
for n in range(0u, n_tps) { cx.uses[n] |= use_repr;}
}
ast_map::node_foreign_item(i@@foreign_item {
node: foreign_item_fn(*),
_
},
abi,
_,
_) => {
if abi.is_intrinsic() {
let nm = cx.ccx.sess.str_of(i.ident);
let name = nm.as_slice();
let flags = if name.starts_with("atomic_") {
0
} else {
match name {
"size_of" | "pref_align_of" | "min_align_of" |
"uninit" | "init" | "transmute" | "move_val" |
"move_val_init" => use_repr,
"get_tydesc" | "needs_drop" | "contains_managed" => use_tydesc,
"visit_tydesc" | "forget" | "frame_address" |
"morestack_addr" => 0,
"offset" |
"memcpy32" | "memcpy64" | "memmove32" | "memmove64" |
"memset32" | "memset64" => use_repr,
"sqrtf32" | "sqrtf64" | "powif32" | "powif64" |
"sinf32" | "sinf64" | "cosf32" | "cosf64" |
"powf32" | "powf64" | "expf32" | "expf64" |
"exp2f32" | "exp2f64" | "logf32" | "logf64" |
"log10f32"| "log10f64"| "log2f32" | "log2f64" |
"fmaf32" | "fmaf64" | "fabsf32" | "fabsf64" |
"floorf32"| "floorf64"| "ceilf32" | "ceilf64" |
"truncf32"| "truncf64" => 0,
"ctpop8" | "ctpop16" | "ctpop32" | "ctpop64" => 0,
"ctlz8" | "ctlz16" | "ctlz32" | "ctlz64" => 0,
"cttz8" | "cttz16" | "cttz32" | "cttz64" => 0,
"bswap16" | "bswap32" | "bswap64" => 0,
"i8_add_with_overflow" | "u8_add_with_overflow" |
"i16_add_with_overflow" | "u16_add_with_overflow" |
"i32_add_with_overflow" | "u32_add_with_overflow" |
"i64_add_with_overflow" | "u64_add_with_overflow" => 0,
"i8_sub_with_overflow" | "u8_sub_with_overflow" |
"i16_sub_with_overflow" | "u16_sub_with_overflow" |
"i32_sub_with_overflow" | "u32_sub_with_overflow" |
"i64_sub_with_overflow" | "u64_sub_with_overflow" => 0,
"i8_mul_with_overflow" | "u8_mul_with_overflow" |
"i16_mul_with_overflow" | "u16_mul_with_overflow" |
"i32_mul_with_overflow" | "u32_mul_with_overflow" |
"i64_mul_with_overflow" | "u64_mul_with_overflow" => 0,
// would be cool to make these an enum instead of
// strings!
_ => fail!("unknown intrinsic in type_use")
}
};
for n in range(0u, n_tps) { cx.uses[n] |= flags;}
}
}
ast_map::node_struct_ctor(*) => {
// Similarly to node_variant, this monomorphized function just
// uses the representations of all of its type parameters.
for n in range(0u, n_tps) { cx.uses[n] |= use_repr; }
}
_ => {
ccx.tcx.sess.bug(fmt!("unknown node type in type_use: %s",
ast_map::node_id_to_str(
ccx.tcx.items,
fn_id_loc.node,
token::get_ident_interner())));
}
}
// Now handle arguments
match ty::get(ty::lookup_item_type(cx.ccx.tcx, fn_id).ty).sty {
ty::ty_bare_fn(ty::BareFnTy {sig: ref sig, _}) |
ty::ty_closure(ty::ClosureTy {sig: ref sig, _}) => {
for arg in sig.inputs.iter() {
type_needs(&cx, use_repr, *arg);
}
}
_ => ()
}
store_type_uses(cx, fn_id)
}
pub fn type_needs(cx: &Context, use_: uint, ty: ty::t) {
// Optimization -- don't descend type if all params already have this use
if cx.uses.iter().any(|&elt| elt & use_ != use_) {
type_needs_inner(cx, use_, ty, @Nil);
}
}
pub fn type_needs_inner(cx: &Context,
use_: uint,
ty: ty::t,
enums_seen: @List<DefId>) {
do ty::maybe_walk_ty(ty) |ty| {
if ty::type_has_params(ty) {
match ty::get(ty).sty {
/*
This previously included ty_box -- that was wrong
because if we cast an @T to an trait (for example) and return
it, we depend on the drop glue for T (we have to write the
right tydesc into the result)
*/
ty::ty_closure(*) |
ty::ty_bare_fn(*) |
ty::ty_ptr(_) |
ty::ty_rptr(_, _) |
ty::ty_trait(*) => false,
ty::ty_enum(did, ref substs) => {
if list::find(enums_seen, |id| *id == did).is_none() {
let seen = @Cons(did, enums_seen);
let r = ty::enum_variants(cx.ccx.tcx, did);
for v in r.iter() {
for aty in v.args.iter() {
let t = ty::subst(cx.ccx.tcx, &(*substs), *aty);
type_needs_inner(cx, use_, t, seen);
}
}
}
false
}
ty::ty_param(p) => {
cx.uses[p.idx] |= use_;
false
}
_ => true
}
} else { false }
}
}
pub fn node_type_needs(cx: &Context, use_: uint, id: NodeId) {
type_needs(cx, use_, ty::node_id_to_type(cx.ccx.tcx, id));
}
pub fn mark_for_method_call(cx: &Context, e_id: NodeId, callee_id: NodeId) {
let mut opt_static_did = None;
{
let r = cx.ccx.maps.method_map.find(&e_id);
for mth in r.iter() {
match mth.origin {
typeck::method_static(did) => {
opt_static_did = Some(did);
}
typeck::method_param(typeck::method_param {
param_num: typeck::param_numbered(param),
_
}) => {
cx.uses[param] |= use_tydesc;
}
_ => (),
}
}
}
// Note: we do not execute this code from within the each() call
// above because the recursive call to `type_needs` can trigger
// inlining and hence can cause `method_map` and
// `node_type_substs` to be modified.
for &did in opt_static_did.iter() {
{
let r = cx.ccx.tcx.node_type_substs.find_copy(&callee_id);
for ts in r.iter() {
let type_uses = type_uses_for(cx.ccx, did, ts.len());
for (uses, subst) in type_uses.iter().zip(ts.iter()) {
type_needs(cx, *uses, *subst)
}
}
}
}
}
pub fn mark_for_expr(cx: &Context, e: &Expr) {
match e.node {
ExprVstore(_, _) | ExprVec(_, _) | ExprStruct(*) | ExprTup(_) |
ExprUnary(_, UnBox(_), _) | ExprUnary(_, UnUniq, _) |
ExprBinary(_, BiAdd, _, _) | ExprRepeat(*) => {
node_type_needs(cx, use_repr, e.id);
}
ExprCast(base, _) => {
let result_t = ty::node_id_to_type(cx.ccx.tcx, e.id);
match ty::get(result_t).sty {
ty::ty_trait(*) => {
// When we're casting to an trait, we need the
// tydesc for the expr that's being cast.
node_type_needs(cx, use_tydesc, base.id);
}
_ => ()
}
}
ExprBinary(_, op, lhs, _) => {
match op {
BiEq | BiLt | BiLe | BiNe | BiGe | BiGt => {
node_type_needs(cx, use_tydesc, lhs.id)
}
_ => ()
}
}
ExprPath(_) | ExprSelf => {
let opt_ts = cx.ccx.tcx.node_type_substs.find_copy(&e.id);
for ts in opt_ts.iter() {
let id = ast_util::def_id_of_def(cx.ccx.tcx.def_map.get_copy(&e.id));
let uses_for_ts = type_uses_for(cx.ccx, id, ts.len());
for (uses, subst) in uses_for_ts.iter().zip(ts.iter()) {
type_needs(cx, *uses, *subst)
}
}
}
ExprFnBlock(*) => {
match ty::ty_closure_sigil(ty::expr_ty(cx.ccx.tcx, e)) {
ast::OwnedSigil => {}
ast::BorrowedSigil | ast::ManagedSigil => {
for fv in freevars::get_freevars(cx.ccx.tcx, e.id).iter() {
let node_id = ast_util::def_id_of_def(fv.def).node;
node_type_needs(cx, use_repr, node_id);
}
}
}
}
ExprAssign(val, _) | ExprAssignOp(_, _, val, _) |
ExprRet(Some(val)) => {
node_type_needs(cx, use_repr, val.id);
}
ExprIndex(callee_id, base, _) => {
// FIXME (#2537): could be more careful and not count fields after
// the chosen field.
let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id);
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
mark_for_method_call(cx, e.id, callee_id);
}
ExprField(base, _, _) => {
// Method calls are now a special syntactic form,
// so `a.b` should always be a field.
assert!(!cx.ccx.maps.method_map.contains_key(&e.id));
let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id);
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
}
ExprCall(f, _, _) => {
let r = ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, f.id));
for a in r.iter() {
type_needs(cx, use_repr, *a);
}
}
ExprMethodCall(callee_id, rcvr, _, _, _, _) => {
let base_ty = ty::node_id_to_type(cx.ccx.tcx, rcvr.id);
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
let r = ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, callee_id));
for a in r.iter() {
type_needs(cx, use_repr, *a);
}
mark_for_method_call(cx, e.id, callee_id);
}
ExprInlineAsm(ref ia) => {
for &(_, input) in ia.inputs.iter() {
node_type_needs(cx, use_repr, input.id);
}
for &(_, out) in ia.outputs.iter() {
node_type_needs(cx, use_repr, out.id);
}
}
ExprParen(e) => mark_for_expr(cx, e),
ExprMatch(*) | ExprBlock(_) | ExprIf(*) | ExprWhile(*) |
ExprBreak(_) | ExprAgain(_) | ExprUnary(*) | ExprLit(_) |
ExprMac(_) | ExprAddrOf(*) | ExprRet(_) | ExprLoop(*) |
ExprDoBody(_) | ExprLogLevel => (),
ExprForLoop(*) => fail!("non-desugared expr_for_loop")
}
}
impl Visitor<()> for Context {
fn visit_expr(&mut self, e:@Expr, _: ()) {
visit::walk_expr(self, e, ());
mark_for_expr(self, e);
}
fn visit_local(&mut self, l:@Local, _:()) {
visit::walk_local(self, l, ());
node_type_needs(self, use_repr, l.id);
}
fn visit_pat(&mut self, p:@Pat, _: ()) {
visit::walk_pat(self, p, ());
node_type_needs(self, use_repr, p.id);
}
fn visit_block(&mut self, b:&Block, _: ()) {
visit::walk_block(self, b, ());
for e in b.expr.iter() {
node_type_needs(self, use_repr, e.id);
}
}
fn visit_item(&mut self, _:@item, _: ()) {
// do nothing
}
}
pub fn handle_body(cx: &mut Context, body: &Block) {
cx.visit_block(body, ());
}