diff --git a/src/comp/back/abi.rs b/src/comp/back/abi.rs index d65a9049786..6a53767491f 100644 --- a/src/comp/back/abi.rs +++ b/src/comp/back/abi.rs @@ -47,30 +47,21 @@ const vec_elt_pad: int = 3; const vec_elt_data: int = 4; const tydesc_field_first_param: int = 0; - const tydesc_field_size: int = 1; - const tydesc_field_align: int = 2; - const tydesc_field_copy_glue: int = 3; - const tydesc_field_drop_glue: int = 4; - const tydesc_field_free_glue: int = 5; - const tydesc_field_sever_glue: int = 6; - const tydesc_field_mark_glue: int = 7; - - // FIXME no longer used in rustc, drop when rustboot is gone const tydesc_field_obj_drop_glue: int = 8; - const tydesc_field_is_stateful: int = 9; - const tydesc_field_cmp_glue: int = 10; - -const n_tydesc_fields: int = 11; +const tydesc_field_shape: int = 11; +const tydesc_field_shape_tables: int = 12; +const tydesc_field_n_params: int = 13; +const n_tydesc_fields: int = 14; const cmp_glue_op_eq: uint = 0u; diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 817e82718ec..0793d390d57 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -895,7 +895,8 @@ fn linearize_ty_params(cx: &@block_ctxt, t: &ty::t) -> fn trans_stack_local_derived_tydesc(cx: &@block_ctxt, llsz: ValueRef, llalign: ValueRef, llroottydesc: ValueRef, - llparamtydescs: ValueRef) -> ValueRef { + llparamtydescs: ValueRef, + n_params: uint) -> ValueRef { let llmyroottydesc = alloca(cx, bcx_ccx(cx).tydesc_type); // By convention, desc 0 is the root descriptor. @@ -904,11 +905,14 @@ fn trans_stack_local_derived_tydesc(cx: &@block_ctxt, llsz: ValueRef, // Store a pointer to the rest of the descriptors. let llfirstparam = cx.build.GEP(llparamtydescs, ~[C_int(0), C_int(0)]); - cx.build.Store(llfirstparam, - cx.build.GEP(llmyroottydesc, ~[C_int(0), C_int(0)])); - cx.build.Store(llsz, cx.build.GEP(llmyroottydesc, ~[C_int(0), C_int(1)])); - cx.build.Store(llalign, - cx.build.GEP(llmyroottydesc, ~[C_int(0), C_int(2)])); + store_inbounds(cx, llfirstparam, llmyroottydesc, + ~[C_int(0), C_int(abi::tydesc_field_first_param)]); + store_inbounds(cx, C_uint(n_params), llmyroottydesc, + ~[C_int(0), C_int(abi::tydesc_field_n_params)]); + store_inbounds(cx, llsz, llmyroottydesc, + ~[C_int(0), C_int(abi::tydesc_field_size)]); + store_inbounds(cx, llalign, llmyroottydesc, + ~[C_int(0), C_int(abi::tydesc_field_align)]); ret llmyroottydesc; } @@ -964,7 +968,8 @@ fn get_derived_tydesc(cx: &@block_ctxt, t: &ty::t, escapes: bool, v = td_val; } else { let llparamtydescs = - alloca(bcx, T_array(T_ptr(bcx_ccx(bcx).tydesc_type), n_params)); + alloca(bcx, T_array(T_ptr(bcx_ccx(bcx).tydesc_type), + n_params + 1u)); let i = 0; for td: ValueRef in tys.descs { let tdp = bcx.build.GEP(llparamtydescs, ~[C_int(0), C_int(i)]); @@ -973,7 +978,7 @@ fn get_derived_tydesc(cx: &@block_ctxt, t: &ty::t, escapes: bool, } v = trans_stack_local_derived_tydesc(bcx, sz.val, align.val, root, - llparamtydescs); + llparamtydescs, n_params); } bcx.fcx.derived_tydescs.insert(t, {lltydesc: v, escapes: escapes}); ret rslt(cx, v); @@ -1191,20 +1196,28 @@ fn emit_tydescs(ccx: &@crate_ctxt) { none. { ccx.stats.n_null_glues += 1u; C_null(cmp_fn_ty) } some(v) { ccx.stats.n_real_glues += 1u; v } }; - let // copy_glue - // drop_glue - // free_glue - // sever_glue - // mark_glue - // obj_drop_glue - // is_stateful - tydesc = + + let shape = shape::shape_of(ccx, pair.key); + let shape_tables = + llvm::LLVMConstPointerCast(ccx.shape_cx.llshapetables, + T_ptr(T_i8())); + + let tydesc = C_named_struct(ccx.tydesc_type, - ~[C_null(T_ptr(T_ptr(ccx.tydesc_type))), ti.size, - ti.align, copy_glue, drop_glue, free_glue, - C_null(glue_fn_ty), C_null(glue_fn_ty), - C_null(glue_fn_ty), C_null(glue_fn_ty), - cmp_glue]); // cmp_glue + ~[C_null(T_ptr(T_ptr(ccx.tydesc_type))), + ti.size, // size + ti.align, // align + copy_glue, // copy_glue + drop_glue, // drop_glue + free_glue, // free_glue + C_null(glue_fn_ty), // sever_glue + C_null(glue_fn_ty), // mark_glue + C_null(glue_fn_ty), // obj_drop_glue + C_null(glue_fn_ty), // is_stateful + cmp_glue, // cmp_glue + C_shape(ccx, shape), // shape + shape_tables, // shape_tables + C_int(0)]); // n_params let gvar = ti.tydesc; llvm::LLVMSetInitializer(gvar, tydesc); @@ -2288,8 +2301,9 @@ fn call_cmp_glue(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: &ty::t, let ti = none[@tydesc_info]; let r = get_tydesc(cx, t, false, ti); lazily_emit_tydesc_glue(cx, abi::tydesc_field_cmp_glue, ti); + let lltydesc = r.val; let lltydescs = - r.bcx.build.GEP(r.val, + r.bcx.build.GEP(lltydesc, ~[C_int(0), C_int(abi::tydesc_field_first_param)]); lltydescs = r.bcx.build.Load(lltydescs); @@ -2297,7 +2311,7 @@ fn call_cmp_glue(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: &ty::t, alt ti { none. { let llfnptr = - r.bcx.build.GEP(r.val, + r.bcx.build.GEP(lltydesc, ~[C_int(0), C_int(abi::tydesc_field_cmp_glue)]); llfn = r.bcx.build.Load(llfnptr); } @@ -2306,7 +2320,7 @@ fn call_cmp_glue(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: &ty::t, let llcmpresultptr = alloca(r.bcx, T_i1()); let llargs: ValueRef[] = - ~[llcmpresultptr, r.bcx.fcx.lltaskptr, C_null(T_ptr(T_nil())), + ~[llcmpresultptr, r.bcx.fcx.lltaskptr, lltydesc, lltydescs, llrawlhsptr, llrawrhsptr, llop]; r.bcx.build.Call(llfn, llargs); ret rslt(r.bcx, r.bcx.build.Load(llcmpresultptr)); @@ -2366,7 +2380,7 @@ fn call_memmove(cx: &@block_ctxt, dst: ValueRef, src: ValueRef, let src_ptr = cx.build.PointerCast(src, T_ptr(T_i8())); let dst_ptr = cx.build.PointerCast(dst, T_ptr(T_i8())); let size = cx.build.IntCast(n_bytes, T_i32()); - let align = C_int(0); + let align = C_int(1); let volatile = C_bool(false); ret rslt(cx, cx.build.Call(memmove, @@ -2397,7 +2411,12 @@ fn memmove_ty(cx: &@block_ctxt, dst: ValueRef, src: ValueRef, t: &ty::t) -> if ty::type_has_dynamic_size(bcx_tcx(cx), t) { let llsz = size_of(cx, t); ret call_memmove(llsz.bcx, dst, src, llsz.val); - } else { ret rslt(cx, cx.build.Store(cx.build.Load(src), dst)); } + } else if ty::type_is_structural(bcx_tcx(cx), t) { + let llsz = llsize_of(type_of(bcx_ccx(cx), cx.sp, t)); + ret call_memmove(cx, dst, src, llsz); + } else { + ret rslt(cx, cx.build.Store(cx.build.Load(src), dst)); + } } // Duplicates any heap-owned memory owned by a value of the given type. @@ -4696,7 +4715,7 @@ fn trans_arg_expr(cx: &@block_ctxt, arg: &ty::arg, lldestty0: TypeRef, // - create_llargs_for_fn_args. // - new_fn_ctxt // - trans_args -fn trans_args(cx: &@block_ctxt, llenv: ValueRef, llobj: &option::t[ValueRef], +fn trans_args(cx: &@block_ctxt, llenv: ValueRef, gen: &option::t[generic_info], lliterbody: &option::t[ValueRef], es: &(@ast::expr)[], fn_ty: &ty::t) -> {bcx: @block_ctxt, args: ValueRef[], retslot: ValueRef} { @@ -4815,8 +4834,7 @@ fn trans_call(cx: &@block_ctxt, f: &@ast::expr, let ret_ty = ty::node_id_to_type(bcx_tcx(cx), id); let args_res = - trans_args(bcx, llenv, f_res.llobj, f_res.generic, lliterbody, args, - fn_ty); + trans_args(bcx, llenv, f_res.generic, lliterbody, args, fn_ty); bcx = args_res.bcx; let llargs = args_res.args; let llretslot = args_res.retslot; @@ -8000,8 +8018,9 @@ fn trans_crate(sess: &session::session, crate: &@ast::crate, tcx: &ty::ctxt, trans_mod(cx, crate.node.module); create_crate_map(ccx); emit_tydescs(ccx); - // Translate the metadata: + shape::gen_shape_tables(ccx); + // Translate the metadata. write_metadata(cx.ccx, crate); if ccx.sess.get_opts().stats { log_err "--- trans stats ---"; diff --git a/src/comp/middle/trans_alt.rs b/src/comp/middle/trans_alt.rs index 4a1734d3bc1..cdfff0ffe9a 100644 --- a/src/comp/middle/trans_alt.rs +++ b/src/comp/middle/trans_alt.rs @@ -433,7 +433,7 @@ fn trans_alt(cx: &@block_ctxt, expr: &@ast::expr, arms: &ast::arm[], ret rslt(cx, cx.build.Unreachable()); } else { - ret rslt(cx, C_nil()); + ret er; } } diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs index 32b25a8a062..e0b26a97c9f 100644 --- a/src/comp/middle/trans_common.rs +++ b/src/comp/middle/trans_common.rs @@ -597,22 +597,13 @@ fn T_tydesc(taskptr_type: TypeRef) -> TypeRef { T_ptr(T_fn(~[T_ptr(T_nil()), taskptr_type, T_ptr(T_nil()), tydescpp, pvoid], T_void())); let cmp_glue_fn_ty = - T_ptr(T_fn(~[T_ptr(T_i1()), taskptr_type, T_ptr(T_nil()), tydescpp, + T_ptr(T_fn(~[T_ptr(T_i1()), taskptr_type, T_ptr(tydesc), tydescpp, pvoid, pvoid, T_i8()], T_void())); - let // first_param - // size - // align - // copy_glue - // drop_glue - // free_glue - // sever_glue - // mark_glue - // obj_drop_glue - // is_stateful - elems = + let elems = ~[tydescpp, T_int(), T_int(), glue_fn_ty, glue_fn_ty, glue_fn_ty, - glue_fn_ty, glue_fn_ty, glue_fn_ty, glue_fn_ty, cmp_glue_fn_ty]; + glue_fn_ty, glue_fn_ty, glue_fn_ty, glue_fn_ty, cmp_glue_fn_ty, + T_ptr(T_i8()), T_ptr(T_i8()), T_int()]; set_struct_body(tydesc, elems); ret tydesc; } @@ -874,3 +865,14 @@ fn C_bytes(bytes : &u8[]) -> ValueRef { ivec::len(bytes), False); } +fn C_shape(ccx : &@crate_ctxt, bytes : &u8[]) -> ValueRef { + let llshape = C_bytes(bytes); + let llglobal = llvm::LLVMAddGlobal(ccx.llmod, val_ty(llshape), + str::buf(ccx.names.next("shape"))); + llvm::LLVMSetInitializer(llglobal, llshape); + llvm::LLVMSetGlobalConstant(llglobal, True); + llvm::LLVMSetLinkage(llglobal, + lib::llvm::LLVMInternalLinkage as llvm::Linkage); + ret llvm::LLVMConstPointerCast(llglobal, T_ptr(T_i8())); +} + diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 556c9e36bea..00f2e6a1b86 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -184,6 +184,7 @@ export type_param; export unify; export variant_info; export walk_ty; +export occurs_check_fails; // Data types tag mode { mo_val; mo_alias(bool); } @@ -655,6 +656,7 @@ fn walk_ty(cx: &ctxt, walker: ty_walk, ty: t) { ty_str. {/* no-op */ } ty_istr. {/* no-op */ } ty_type. {/* no-op */ } + ty_task. {/* no-op */ } ty_native(_) {/* no-op */ } ty_box(tm) { walk_ty(cx, walker, tm.ty); } ty_vec(tm) { walk_ty(cx, walker, tm.ty); } @@ -686,6 +688,9 @@ fn walk_ty(cx: &ctxt, walker: ty_walk, ty: t) { walk_ty(cx, walker, sub); for tp: t in tps { walk_ty(cx, walker, tp); } } + ty_constr(sub, _) { + walk_ty(cx, walker, sub); + } ty_var(_) {/* no-op */ } ty_param(_,_) {/* no-op */ } } @@ -1393,6 +1398,24 @@ fn type_param(cx: &ctxt, ty: &t) -> option::t[uint] { ret none; } +// Returns an ivec of all the type variables +// occurring in t. It may contain duplicates. +fn vars_in_type(cx:&ctxt, ty: &t) -> int[] { + fn collect_var(cx:&ctxt, vars: &@mutable int[], ty: t) { + alt struct(cx, ty) { + ty_var(v) { + *vars += ~[v]; + } + _ {} + } + } + let rslt: @mutable int[] = @mutable (~[]); + walk_ty(cx, bind collect_var(cx, rslt, _), ty); + // Works because of a "convenient" bug that lets us + // return a mutable ivec as if it's immutable + ret *rslt; +} + fn type_autoderef(cx: &ctxt, t: &ty::t) -> ty::t { let t1: ty::t = t; while true { @@ -1990,6 +2013,32 @@ fn is_lval(expr: &@ast::expr) -> bool { } } +fn occurs_check_fails(tcx: &ctxt, sp: &option::t[span], vid: int, rt: &t) + -> bool { + if (!type_contains_vars(tcx, rt)) { + // Fast path + ret false; + } + // Occurs check! + if ivec::member(vid, vars_in_type(tcx, rt)) { + alt sp { + some (s) { + // Maybe this should be span_err -- however, there's an + // assertion later on that the type doesn't contain + // variables, so in this case we have to be sure to die. + tcx.sess.span_fatal(s, + "Type inference failed because I \ + could not find a type\n that's both of the form " + + ty_to_str(tcx, ty::mk_var(tcx, (vid))) + + " and of the form " + ty_to_str(tcx, rt) + + ". Such a type would have to be infinitely \ + large."); + } + _ { ret true; } + } + } + else { ret false; } +} // Type unification via Robinson's algorithm (Robinson 1965). Implemented as // described in Hoder and Voronkov: @@ -2318,9 +2367,6 @@ mod unify { // TODO: rewrite this using tuple pattern matching when available, to // avoid all this rightward drift and spikiness. - // TODO: occurs check, to make sure we don't loop forever when - // unifying e.g. 'a and option['a] - // Fast path. if eq_ty(expected, actual) { ret ures_ok(expected); } @@ -2694,9 +2740,15 @@ mod unify { } // Fixups and substitutions - fn fixup_vars(tcx: ty_ctxt, vb: @var_bindings, typ: t) -> fixup_result { - fn subst_vars(tcx: ty_ctxt, vb: @var_bindings, + // Takes an optional span - complain about occurs check violations + // iff the span is present (so that if we already know we're going + // to error anyway, we don't complain) + fn fixup_vars(tcx: ty_ctxt, sp: &option::t[span], + vb: @var_bindings, typ: t) -> fixup_result { + fn subst_vars(tcx: ty_ctxt, sp: &option::t[span], vb: @var_bindings, unresolved: @mutable option::t[int], vid: int) -> t { + // Should really return a fixup_result instead of a t, but fold_ty + // doesn't allow returning anything but a t. if vid as uint >= ufindivec::set_count(vb.sets) { *unresolved = some(vid); ret ty::mk_var(tcx, vid); @@ -2705,15 +2757,18 @@ mod unify { alt smallintmap::find[t](vb.types, root_id) { none. { *unresolved = some(vid); ret ty::mk_var(tcx, vid); } some(rt) { + if occurs_check_fails(tcx, sp, vid, rt) { + // Return the type unchanged, so we can error out downstream + ret rt; + } ret fold_ty(tcx, - fm_var(bind subst_vars(tcx, vb, unresolved, _)), - rt); + fm_var(bind subst_vars(tcx, sp, vb, unresolved, _)), rt); } } } let unresolved = @mutable none[int]; let rty = - fold_ty(tcx, fm_var(bind subst_vars(tcx, vb, unresolved, _)), + fold_ty(tcx, fm_var(bind subst_vars(tcx, sp, vb, unresolved, _)), typ); let ur = *unresolved; alt ur { @@ -2721,13 +2776,14 @@ mod unify { some(var_id) { ret fix_err(var_id); } } } - fn resolve_type_var(tcx: &ty_ctxt, vb: &@var_bindings, vid: int) -> + fn resolve_type_var(tcx: &ty_ctxt, sp: &option::t[span], + vb: &@var_bindings, vid: int) -> fixup_result { if vid as uint >= ufindivec::set_count(vb.sets) { ret fix_err(vid); } let root_id = ufindivec::find(vb.sets, vid as uint); alt smallintmap::find[t](vb.types, root_id) { none. { ret fix_err(vid); } - some(rt) { ret fixup_vars(tcx, vb, rt); } + some(rt) { ret fixup_vars(tcx, sp, vb, rt); } } } } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index d5bc8b0cbe3..e5a8f7eba8a 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -879,7 +879,18 @@ fn do_autoderef(fcx: &@fn_ctxt, sp: &span, t: &ty::t) -> ty::t { let t1 = t; while true { alt structure_of(fcx, sp, t1) { - ty::ty_box(inner) { t1 = inner.ty; } + ty::ty_box(inner) { + alt ty::struct(fcx.ccx.tcx, t1) { + ty::ty_var(v1) { + if ty::occurs_check_fails(fcx.ccx.tcx, some(sp), v1, + ty::mk_box(fcx.ccx.tcx, inner)) { + break; + } + } + _ {} + } + t1 = inner.ty; + } ty::ty_res(_, inner, tps) { t1 = ty::substitute_type_params(fcx.ccx.tcx, tps, inner); } @@ -942,7 +953,7 @@ fn do_fn_block_coerce(fcx: &@fn_ctxt, sp: &span, actual: &ty::t, fn resolve_type_vars_if_possible(fcx: &@fn_ctxt, typ: ty::t) -> ty::t { - alt ty::unify::fixup_vars(fcx.ccx.tcx, fcx.var_bindings, typ) { + alt ty::unify::fixup_vars(fcx.ccx.tcx, none, fcx.var_bindings, typ) { fix_ok(new_type) { ret new_type; } fix_err(_) { ret typ; } } @@ -1073,7 +1084,8 @@ mod writeback { fn resolve_type_vars_in_type(fcx: &@fn_ctxt, sp: &span, typ: ty::t) -> option::t[ty::t] { if !ty::type_contains_vars(fcx.ccx.tcx, typ) { ret some(typ); } - alt ty::unify::fixup_vars(fcx.ccx.tcx, fcx.var_bindings, typ) { + alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp), + fcx.var_bindings, typ) { fix_ok(new_type) { ret some(new_type); } fix_err(vid) { fcx.ccx.tcx.sess.span_err(sp, @@ -1139,7 +1151,7 @@ mod writeback { if !wbcx.success { ret; } let var_id = lookup_local(wbcx.fcx, l.span, l.node.id); let fix_rslt = - ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, + ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span), wbcx.fcx.var_bindings, var_id); alt fix_rslt { fix_ok(lty) { write::ty_only(wbcx.fcx.ccx.tcx, l.node.id, lty); } diff --git a/src/test/compile-fail/occurs-check-2.rs b/src/test/compile-fail/occurs-check-2.rs new file mode 100644 index 00000000000..9b5536e57c3 --- /dev/null +++ b/src/test/compile-fail/occurs-check-2.rs @@ -0,0 +1,5 @@ +// error-pattern: Type inference failed because I could not find +fn main() { + let f = @f; + f(); +} \ No newline at end of file diff --git a/src/test/compile-fail/occurs-check.rs b/src/test/compile-fail/occurs-check.rs new file mode 100644 index 00000000000..a2bd12fcc35 --- /dev/null +++ b/src/test/compile-fail/occurs-check.rs @@ -0,0 +1,4 @@ +// error-pattern: Type inference failed because I could not find +fn main() { + let f = @f; +} diff --git a/src/test/run-pass/alt-bot-2.rs b/src/test/run-pass/alt-bot-2.rs new file mode 100644 index 00000000000..3183b580fc6 --- /dev/null +++ b/src/test/run-pass/alt-bot-2.rs @@ -0,0 +1,3 @@ +// n.b. This was only ever failing with optimization disabled. +fn a() -> int { alt ret 1 { 2 { 3 } } } +fn main() { a(); }