Convert rustc::util to istrs. Issue #855
This commit is contained in:
parent
54691f9a6c
commit
d2ae28fc99
12 changed files with 221 additions and 199 deletions
|
|
@ -118,7 +118,7 @@ fn time<@T>(do_it: bool, what: str, thunk: fn() -> T) -> T {
|
|||
let rv = thunk();
|
||||
let end = std::time::precise_time_s();
|
||||
log_err #fmt["time: %s took %s s", what,
|
||||
common::float_to_str(end - start, 3u)];
|
||||
istr::to_estr(common::float_to_str(end - start, 3u))];
|
||||
ret rv;
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +184,9 @@ fn pretty_print_input(sess: session::session, cfg: ast::crate_cfg, input: str,
|
|||
pp::space(s.s);
|
||||
pp::word(s.s, "as");
|
||||
pp::space(s.s);
|
||||
pp::word(s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr)));
|
||||
pp::word(
|
||||
s.s,
|
||||
istr::to_estr(ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr))));
|
||||
pprust::pclose(s);
|
||||
}
|
||||
_ { }
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ fn default_native_lib_naming(sess: session::session, static: bool) ->
|
|||
}
|
||||
|
||||
fn find_library_crate(sess: &session::session, ident: &ast::ident,
|
||||
metas: &[@ast::meta_item], library_search_paths: &[istr])
|
||||
metas: &[@ast::meta_item],
|
||||
library_search_paths: &[istr])
|
||||
-> option::t<{ident: istr, data: @[u8]}> {
|
||||
|
||||
attr::require_unique_names(sess, metas);
|
||||
|
|
|
|||
|
|
@ -390,9 +390,10 @@ fn check_for(cx: &ctx, local: &@ast::local, seq: &@ast::expr, blk: &ast::blk,
|
|||
ty::ty_vec(mt) { if mt.mut != ast::imm { unsafe = [seq_t]; } }
|
||||
ty::ty_str. | ty::ty_istr. {/* no-op */ }
|
||||
_ {
|
||||
cx.tcx.sess.span_unimpl(seq.span,
|
||||
"unknown seq type " +
|
||||
util::ppaux::ty_to_str(cx.tcx, seq_t));
|
||||
cx.tcx.sess.span_unimpl(
|
||||
seq.span,
|
||||
"unknown seq type " +
|
||||
istr::to_estr(util::ppaux::ty_to_str(cx.tcx, seq_t)));
|
||||
}
|
||||
}
|
||||
let bindings = ast_util::pat_binding_ids(local.node.pat);
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ import syntax::visit;
|
|||
|
||||
import std::vec;
|
||||
import std::option;
|
||||
import std::istr;
|
||||
|
||||
import ast::kind;
|
||||
import ast::kind_unique;
|
||||
|
|
@ -115,13 +116,13 @@ fn need_expr_kind(tcx: &ty::ctxt, e: &@ast::expr, k_need: ast::kind,
|
|||
let tk = type_and_kind(tcx, e);
|
||||
log #fmt["for %s: want %s type, got %s type %s", descr,
|
||||
kind_to_str(k_need), kind_to_str(tk.kind),
|
||||
util::ppaux::ty_to_str(tcx, tk.ty)];
|
||||
istr::to_estr(util::ppaux::ty_to_str(tcx, tk.ty))];
|
||||
|
||||
if !kind_lteq(k_need, tk.kind) {
|
||||
let s =
|
||||
#fmt["mismatched kinds for %s: needed %s type, got %s type %s",
|
||||
descr, kind_to_str(k_need), kind_to_str(tk.kind),
|
||||
util::ppaux::ty_to_str(tcx, tk.ty)];
|
||||
istr::to_estr(util::ppaux::ty_to_str(tcx, tk.ty))];
|
||||
tcx.sess.span_err(e.span, s);
|
||||
}
|
||||
}
|
||||
|
|
@ -170,7 +171,7 @@ fn check_expr(tcx: &ty::ctxt, e: &@ast::expr) {
|
|||
#fmt["mismatched kinds for typaram %d: \
|
||||
needed %s type, got %s type %s",
|
||||
i, kind_to_str(k_need), kind_to_str(k),
|
||||
util::ppaux::ty_to_str(tcx, t)];
|
||||
istr::to_estr(util::ppaux::ty_to_str(tcx, t))];
|
||||
tcx.sess.span_err(e.span, s);
|
||||
}
|
||||
i += 1;
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ fn type_of(cx: &@crate_ctxt, sp: &span, t: ty::t) -> TypeRef {
|
|||
if ty::type_has_dynamic_size(cx.tcx, t) {
|
||||
cx.sess.span_fatal(sp,
|
||||
"type_of() called on a type with dynamic size: " +
|
||||
ty_to_str(cx.tcx, t));
|
||||
istr::to_estr(ty_to_str(cx.tcx, t)));
|
||||
}
|
||||
ret type_of_inner(cx, sp, t);
|
||||
}
|
||||
|
|
@ -1060,12 +1060,13 @@ fn get_tydesc(cx: &@block_ctxt, orig_t: ty::t, escapes: bool,
|
|||
if id < vec::len(cx.fcx.lltydescs) {
|
||||
ret {kind: tk_param, result: rslt(cx, cx.fcx.lltydescs[id])};
|
||||
} else {
|
||||
bcx_tcx(cx).sess.span_bug(cx.sp,
|
||||
"Unbound typaram in get_tydesc: " +
|
||||
"orig_t = " +
|
||||
ty_to_str(bcx_tcx(cx), orig_t) +
|
||||
" ty_param = " +
|
||||
istr::to_estr(std::uint::str(id)));
|
||||
bcx_tcx(cx).sess.span_bug(
|
||||
cx.sp,
|
||||
"Unbound typaram in get_tydesc: " +
|
||||
"orig_t = " +
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), orig_t)) +
|
||||
" ty_param = " +
|
||||
istr::to_estr(std::uint::str(id)));
|
||||
}
|
||||
}
|
||||
none. {/* fall through */ }
|
||||
|
|
@ -1129,7 +1130,7 @@ fn set_glue_inlining(cx: &@local_ctxt, f: ValueRef, t: ty::t) {
|
|||
// Generates the declaration for (but doesn't emit) a type descriptor.
|
||||
fn declare_tydesc(cx: &@local_ctxt, sp: &span, t: ty::t, ty_params: &[uint])
|
||||
-> @tydesc_info {
|
||||
log "+++ declare_tydesc " + ty_to_str(cx.ccx.tcx, t);
|
||||
log ~"+++ declare_tydesc " + ty_to_str(cx.ccx.tcx, t);
|
||||
let ccx = cx.ccx;
|
||||
let llsize;
|
||||
let llalign;
|
||||
|
|
@ -1163,7 +1164,7 @@ fn declare_tydesc(cx: &@local_ctxt, sp: &span, t: ty::t, ty_params: &[uint])
|
|||
mutable cmp_glue: none::<ValueRef>,
|
||||
mutable copy_glue: none::<ValueRef>,
|
||||
ty_params: ty_params};
|
||||
log "--- declare_tydesc " + ty_to_str(cx.ccx.tcx, t);
|
||||
log ~"--- declare_tydesc " + ty_to_str(cx.ccx.tcx, t);
|
||||
ret info;
|
||||
}
|
||||
|
||||
|
|
@ -2029,9 +2030,9 @@ fn iter_sequence(cx: @block_ctxt, v: ValueRef, t: ty::t, f: &val_and_ty_fn)
|
|||
ret iter_sequence_body(cx, v, et, f, true, true);
|
||||
}
|
||||
_ {
|
||||
bcx_ccx(cx).sess.bug("unexpected type in \
|
||||
trans::iter_sequence: "
|
||||
+ ty_to_str(cx.fcx.lcx.ccx.tcx, t));
|
||||
bcx_ccx(cx).sess.bug(
|
||||
"unexpected type in trans::iter_sequence: "
|
||||
+ istr::to_estr(ty_to_str(cx.fcx.lcx.ccx.tcx, t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2062,7 +2063,7 @@ fn lazily_emit_tydesc_glue(cx: &@block_ctxt, field: int,
|
|||
some(_) { }
|
||||
none. {
|
||||
log #fmt["+++ lazily_emit_tydesc_glue TAKE %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
let lcx = cx.fcx.lcx;
|
||||
let glue_fn =
|
||||
declare_generic_glue(lcx, ti.ty, T_glue_fn(*lcx.ccx),
|
||||
|
|
@ -2072,7 +2073,7 @@ fn lazily_emit_tydesc_glue(cx: &@block_ctxt, field: int,
|
|||
default_helper(make_take_glue),
|
||||
ti.ty_params, ~"take");
|
||||
log #fmt["--- lazily_emit_tydesc_glue TAKE %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
}
|
||||
}
|
||||
} else if field == abi::tydesc_field_drop_glue {
|
||||
|
|
@ -2080,7 +2081,7 @@ fn lazily_emit_tydesc_glue(cx: &@block_ctxt, field: int,
|
|||
some(_) { }
|
||||
none. {
|
||||
log #fmt["+++ lazily_emit_tydesc_glue DROP %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
let lcx = cx.fcx.lcx;
|
||||
let glue_fn =
|
||||
declare_generic_glue(lcx, ti.ty, T_glue_fn(*lcx.ccx),
|
||||
|
|
@ -2090,7 +2091,7 @@ fn lazily_emit_tydesc_glue(cx: &@block_ctxt, field: int,
|
|||
default_helper(make_drop_glue),
|
||||
ti.ty_params, ~"drop");
|
||||
log #fmt["--- lazily_emit_tydesc_glue DROP %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
}
|
||||
}
|
||||
} else if field == abi::tydesc_field_free_glue {
|
||||
|
|
@ -2098,7 +2099,7 @@ fn lazily_emit_tydesc_glue(cx: &@block_ctxt, field: int,
|
|||
some(_) { }
|
||||
none. {
|
||||
log #fmt["+++ lazily_emit_tydesc_glue FREE %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
let lcx = cx.fcx.lcx;
|
||||
let glue_fn =
|
||||
declare_generic_glue(lcx, ti.ty, T_glue_fn(*lcx.ccx),
|
||||
|
|
@ -2108,7 +2109,7 @@ fn lazily_emit_tydesc_glue(cx: &@block_ctxt, field: int,
|
|||
default_helper(make_free_glue),
|
||||
ti.ty_params, ~"free");
|
||||
log #fmt["--- lazily_emit_tydesc_glue FREE %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
}
|
||||
}
|
||||
} else if field == abi::tydesc_field_cmp_glue {
|
||||
|
|
@ -2116,10 +2117,10 @@ fn lazily_emit_tydesc_glue(cx: &@block_ctxt, field: int,
|
|||
some(_) { }
|
||||
none. {
|
||||
log #fmt["+++ lazily_emit_tydesc_glue CMP %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
ti.cmp_glue = some(bcx_ccx(cx).upcalls.cmp_type);
|
||||
log #fmt["--- lazily_emit_tydesc_glue CMP %s",
|
||||
ty_to_str(bcx_tcx(cx), ti.ty)];
|
||||
istr::to_estr(ty_to_str(bcx_tcx(cx), ti.ty))];
|
||||
}
|
||||
}
|
||||
} else if field == abi::tydesc_field_copy_glue {
|
||||
|
|
@ -2421,7 +2422,7 @@ fn copy_val_no_check(cx: &@block_ctxt, action: copy_action, dst: ValueRef,
|
|||
}
|
||||
}
|
||||
ccx.sess.bug("unexpected type in trans::copy_val_no_check: " +
|
||||
ty_to_str(ccx.tcx, t));
|
||||
istr::to_estr(ty_to_str(ccx.tcx, t)));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2468,7 +2469,7 @@ fn move_val(cx: @block_ctxt, action: copy_action, dst: ValueRef,
|
|||
}
|
||||
}
|
||||
bcx_ccx(cx).sess.bug("unexpected type in trans::move_val: " +
|
||||
ty_to_str(tcx, t));
|
||||
istr::to_estr(ty_to_str(tcx, t)));
|
||||
}
|
||||
|
||||
fn move_val_if_temp(cx: @block_ctxt, action: copy_action, dst: ValueRef,
|
||||
|
|
@ -4627,9 +4628,8 @@ fn trans_fail_expr(cx: &@block_ctxt, sp_opt: &option::t<span>,
|
|||
ret trans_fail_value(bcx, sp_opt, elt);
|
||||
} else {
|
||||
bcx_ccx(cx).sess.span_bug(expr.span,
|
||||
"fail called with unsupported \
|
||||
type "
|
||||
+ ty_to_str(tcx, e_ty));
|
||||
"fail called with unsupported type "
|
||||
+ istr::to_estr(ty_to_str(tcx, e_ty)));
|
||||
}
|
||||
}
|
||||
_ { ret trans_fail(bcx, sp_opt, ~"explicit failure"); }
|
||||
|
|
|
|||
|
|
@ -1119,8 +1119,9 @@ fn callee_modes(fcx: &fn_ctxt, callee: node_id) -> [ty::mode] {
|
|||
}
|
||||
_ {
|
||||
// Shouldn't happen; callee should be ty_fn.
|
||||
fcx.ccx.tcx.sess.bug("non-fn callee type in callee_modes: " +
|
||||
util::ppaux::ty_to_str(fcx.ccx.tcx, ty));
|
||||
fcx.ccx.tcx.sess.bug(
|
||||
"non-fn callee type in callee_modes: " +
|
||||
istr::to_estr(util::ppaux::ty_to_str(fcx.ccx.tcx, ty)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ fn add_constraint(tcx: &ty::ctxt, c: sp_constr, next: uint, tbl: constr_map)
|
|||
to a bit number in the precondition/postcondition vectors */
|
||||
fn mk_fn_info(ccx: &crate_ctxt, f: &_fn, tp: &[ty_param], f_sp: &span,
|
||||
f_name: &fn_ident, id: node_id) {
|
||||
let name = istr::from_estr(fn_ident_to_string(id, f_name));
|
||||
let name = fn_ident_to_string(id, f_name);
|
||||
let res_map = @new_def_hash::<constraint>();
|
||||
let next: uint = 0u;
|
||||
|
||||
|
|
|
|||
|
|
@ -751,7 +751,7 @@ fn fn_pre_post(f: &_fn, tps: &[ty_param], sp: &span, i: &fn_ident,
|
|||
let fcx =
|
||||
{enclosing: ccx.fm.get(id),
|
||||
id: id,
|
||||
name: istr::from_estr(fn_ident_to_string(id, i)),
|
||||
name: fn_ident_to_string(id, i),
|
||||
ccx: ccx};
|
||||
find_pre_post_fn(fcx, f);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -896,7 +896,8 @@ fn get_element_type(cx: &ctxt, ty: t, i: uint) -> t {
|
|||
ty_rec(flds) { ret flds[i].mt.ty; }
|
||||
ty_tup(ts) { ret ts[i]; }
|
||||
_ {
|
||||
cx.sess.bug("get_element_type called on type " + ty_to_str(cx, ty) +
|
||||
cx.sess.bug("get_element_type called on type " +
|
||||
istr::to_estr(ty_to_str(cx, ty)) +
|
||||
" - expected a \
|
||||
tuple or record");
|
||||
}
|
||||
|
|
@ -1120,7 +1121,7 @@ fn type_kind(cx: &ctxt, ty: t) -> ast::kind {
|
|||
|
||||
|
||||
_ {
|
||||
cx.sess.bug("missed case: " + ty_to_str(cx, ty));
|
||||
cx.sess.bug("missed case: " + istr::to_estr(ty_to_str(cx, ty)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1848,8 +1849,9 @@ fn occurs_check_fails(tcx: &ctxt, sp: &option::t<span>, vid: int, rt: t) ->
|
|||
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) +
|
||||
+ istr::to_estr(ty_to_str(tcx, ty::mk_var(tcx, vid))) +
|
||||
" and of the form " +
|
||||
istr::to_estr(ty_to_str(tcx, rt)) +
|
||||
". Such a type would have to be infinitely \
|
||||
large.");
|
||||
}
|
||||
|
|
@ -2541,7 +2543,9 @@ mod unify {
|
|||
let typespec;
|
||||
alt smallintmap::find::<t>(vb.types, i) {
|
||||
none. { typespec = ""; }
|
||||
some(typ) { typespec = " =" + ty_to_str(tcx, typ); }
|
||||
some(typ) {
|
||||
typespec = " =" + istr::to_estr(ty_to_str(tcx, typ));
|
||||
}
|
||||
}
|
||||
log_err #fmt["set %u:%s%s", i, typespec, sets];
|
||||
i += 1u;
|
||||
|
|
@ -2598,54 +2602,54 @@ mod unify {
|
|||
}
|
||||
}
|
||||
|
||||
fn type_err_to_str(err: &ty::type_err) -> str {
|
||||
fn type_err_to_str(err: &ty::type_err) -> istr {
|
||||
alt err {
|
||||
terr_mismatch. { ret "types differ"; }
|
||||
terr_mismatch. { ret ~"types differ"; }
|
||||
terr_controlflow_mismatch. {
|
||||
ret "returning function used where non-returning function" +
|
||||
" was expected";
|
||||
ret ~"returning function used where non-returning function" +
|
||||
~" was expected";
|
||||
}
|
||||
terr_box_mutability. { ret "boxed values differ in mutability"; }
|
||||
terr_vec_mutability. { ret "vectors differ in mutability"; }
|
||||
terr_box_mutability. { ret ~"boxed values differ in mutability"; }
|
||||
terr_vec_mutability. { ret ~"vectors differ in mutability"; }
|
||||
terr_tuple_size(e_sz, a_sz) {
|
||||
ret istr::to_estr(~"expected a tuple with " +
|
||||
uint::to_str(e_sz, 10u) +
|
||||
~" elements but found one with " +
|
||||
uint::to_str(a_sz, 10u) +
|
||||
~" elements");
|
||||
ret ~"expected a tuple with " +
|
||||
uint::to_str(e_sz, 10u) +
|
||||
~" elements but found one with " +
|
||||
uint::to_str(a_sz, 10u) +
|
||||
~" elements";
|
||||
}
|
||||
terr_record_size(e_sz, a_sz) {
|
||||
ret istr::to_estr(~"expected a record with " +
|
||||
uint::to_str(e_sz, 10u) +
|
||||
~" fields but found one with " +
|
||||
uint::to_str(a_sz, 10u) +
|
||||
~" fields");
|
||||
ret ~"expected a record with " +
|
||||
uint::to_str(e_sz, 10u) +
|
||||
~" fields but found one with " +
|
||||
uint::to_str(a_sz, 10u) +
|
||||
~" fields";
|
||||
}
|
||||
terr_record_mutability. { ret "record elements differ in mutability"; }
|
||||
terr_record_mutability. { ret ~"record elements differ in mutability"; }
|
||||
terr_record_fields(e_fld, a_fld) {
|
||||
ret "expected a record with field '" + istr::to_estr(e_fld) +
|
||||
"' but found one with field '" + istr::to_estr(a_fld) + "'";
|
||||
ret ~"expected a record with field '" + e_fld +
|
||||
~"' but found one with field '" + a_fld + ~"'";
|
||||
}
|
||||
terr_arg_count. { ret "incorrect number of function parameters"; }
|
||||
terr_meth_count. { ret "incorrect number of object methods"; }
|
||||
terr_arg_count. { ret ~"incorrect number of function parameters"; }
|
||||
terr_meth_count. { ret ~"incorrect number of object methods"; }
|
||||
terr_obj_meths(e_meth, a_meth) {
|
||||
ret "expected an obj with method '" + istr::to_estr(e_meth) +
|
||||
"' but found one with method '" + istr::to_estr(a_meth) + "'";
|
||||
ret ~"expected an obj with method '" + e_meth +
|
||||
~"' but found one with method '" + a_meth + ~"'";
|
||||
}
|
||||
terr_mode_mismatch(e_mode, a_mode) {
|
||||
ret "expected argument mode " + mode_str_1(e_mode) + " but found " +
|
||||
ret ~"expected argument mode " + mode_str_1(e_mode) + ~" but found " +
|
||||
mode_str_1(a_mode);
|
||||
}
|
||||
terr_constr_len(e_len, a_len) {
|
||||
ret istr::to_estr(~"Expected a type with " +
|
||||
uint::str(e_len) +
|
||||
~" constraints, but found one with " +
|
||||
uint::str(a_len) + ~" constraints");
|
||||
ret ~"Expected a type with " +
|
||||
uint::str(e_len) +
|
||||
~" constraints, but found one with " +
|
||||
uint::str(a_len) + ~" constraints";
|
||||
}
|
||||
terr_constr_mismatch(e_constr, a_constr) {
|
||||
ret "Expected a type with constraint " + ty_constr_to_str(e_constr) +
|
||||
" but found one with constraint " +
|
||||
ty_constr_to_str(a_constr);
|
||||
ret ~"Expected a type with constraint " + ty_constr_to_str(e_constr) +
|
||||
~" but found one with constraint " +
|
||||
ty_constr_to_str(a_constr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2781,7 +2785,7 @@ fn ret_ty_of_fn_ty(cx: ctxt, a_ty: t) -> t {
|
|||
ty::ty_native_fn(_, _, ret_ty) { ret ret_ty; }
|
||||
_ {
|
||||
cx.sess.bug("ret_ty_of_fn_ty() called on non-function type: " +
|
||||
ty_to_str(cx, a_ty));
|
||||
istr::to_estr(ty_to_str(cx, a_ty)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1010,12 +1010,14 @@ mod demand {
|
|||
ures_err(err) {
|
||||
let e_err = resolve_type_vars_if_possible(fcx, expected);
|
||||
let a_err = resolve_type_vars_if_possible(fcx, actual);
|
||||
fcx.ccx.tcx.sess.span_err(sp,
|
||||
"mismatched types: expected " +
|
||||
ty_to_str(fcx.ccx.tcx, e_err) +
|
||||
" but found " +
|
||||
ty_to_str(fcx.ccx.tcx, a_err) + " ("
|
||||
+ ty::type_err_to_str(err) + ")");
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
sp,
|
||||
"mismatched types: expected " +
|
||||
istr::to_estr(ty_to_str(fcx.ccx.tcx, e_err)) +
|
||||
" but found " +
|
||||
istr::to_estr(ty_to_str(fcx.ccx.tcx, a_err)) + " ("
|
||||
+ istr::to_estr(ty::type_err_to_str(err))
|
||||
+ ")");
|
||||
ret mk_result(fcx, expected, ty_param_subst_var_ids);
|
||||
}
|
||||
}
|
||||
|
|
@ -1398,11 +1400,10 @@ fn check_pat(fcx: &@fn_ctxt, map: &ast_util::pat_id_map, pat: &@ast::pat,
|
|||
_ {
|
||||
// FIXME: Switch expected and actual in this message? I
|
||||
// can never tell.
|
||||
fcx.ccx.tcx.sess.span_fatal(pat.span,
|
||||
#fmt["mismatched types: \
|
||||
expected %s, found tag",
|
||||
ty_to_str(fcx.ccx.tcx,
|
||||
expected)]);
|
||||
fcx.ccx.tcx.sess.span_fatal(
|
||||
pat.span,
|
||||
#fmt["mismatched types: expected %s, found tag",
|
||||
istr::to_estr(ty_to_str(fcx.ccx.tcx, expected))]);
|
||||
}
|
||||
}
|
||||
write::ty_fixup(fcx, pat.id, path_tpot);
|
||||
|
|
@ -1412,11 +1413,10 @@ fn check_pat(fcx: &@fn_ctxt, map: &ast_util::pat_id_map, pat: &@ast::pat,
|
|||
alt structure_of(fcx, pat.span, expected) {
|
||||
ty::ty_rec(fields) { ex_fields = fields; }
|
||||
_ {
|
||||
fcx.ccx.tcx.sess.span_fatal(pat.span,
|
||||
#fmt["mismatched types: expected %s, \
|
||||
found record",
|
||||
ty_to_str(fcx.ccx.tcx,
|
||||
expected)]);
|
||||
fcx.ccx.tcx.sess.span_fatal(
|
||||
pat.span,
|
||||
#fmt["mismatched types: expected %s, found record",
|
||||
istr::to_estr(ty_to_str(fcx.ccx.tcx, expected))]);
|
||||
}
|
||||
}
|
||||
let f_count = vec::len(fields);
|
||||
|
|
@ -1450,11 +1450,10 @@ fn check_pat(fcx: &@fn_ctxt, map: &ast_util::pat_id_map, pat: &@ast::pat,
|
|||
alt structure_of(fcx, pat.span, expected) {
|
||||
ty::ty_tup(elts) { ex_elts = elts; }
|
||||
_ {
|
||||
fcx.ccx.tcx.sess.span_fatal(pat.span,
|
||||
#fmt["mismatched types: expected %s, \
|
||||
found tuple",
|
||||
ty_to_str(fcx.ccx.tcx,
|
||||
expected)]);
|
||||
fcx.ccx.tcx.sess.span_fatal(
|
||||
pat.span,
|
||||
#fmt["mismatched types: expected %s, found tuple",
|
||||
istr::to_estr(ty_to_str(fcx.ccx.tcx, expected))]);
|
||||
}
|
||||
}
|
||||
let e_count = vec::len(elts);
|
||||
|
|
@ -1477,10 +1476,11 @@ fn check_pat(fcx: &@fn_ctxt, map: &ast_util::pat_id_map, pat: &@ast::pat,
|
|||
write::ty_only_fixup(fcx, pat.id, expected);
|
||||
}
|
||||
_ {
|
||||
fcx.ccx.tcx.sess.span_fatal(pat.span,
|
||||
"mismatched types: expected " +
|
||||
ty_to_str(fcx.ccx.tcx, expected) +
|
||||
" found box");
|
||||
fcx.ccx.tcx.sess.span_fatal(
|
||||
pat.span,
|
||||
"mismatched types: expected " +
|
||||
istr::to_estr(ty_to_str(fcx.ccx.tcx, expected)) +
|
||||
" found box");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1578,11 +1578,12 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
arg_tys
|
||||
}
|
||||
_ {
|
||||
fcx.ccx.tcx.sess.span_fatal(f.span,
|
||||
"mismatched types: \
|
||||
expected function or native \
|
||||
function but found "
|
||||
+ ty_to_str(fcx.ccx.tcx, fty))
|
||||
fcx.ccx.tcx.sess.span_fatal(
|
||||
f.span,
|
||||
"mismatched types: \
|
||||
expected function or native \
|
||||
function but found "
|
||||
+ istr::to_estr(ty_to_str(fcx.ccx.tcx, fty)))
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1790,7 +1791,8 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
let t_str = ty_to_str(fcx.ccx.tcx, resolved_t);
|
||||
let errmsg =
|
||||
"binary operation " + binopstr +
|
||||
" cannot be applied to type `" + t_str + "`";
|
||||
" cannot be applied to type `" +
|
||||
istr::to_estr(t_str) + "`";
|
||||
fcx.ccx.tcx.sess.span_err(span, errmsg);
|
||||
}
|
||||
}
|
||||
|
|
@ -1848,20 +1850,22 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
}
|
||||
ty::ty_ptr(inner) { oper_t = inner.ty; }
|
||||
_ {
|
||||
tcx.sess.span_fatal(expr.span,
|
||||
"dereferencing non-" +
|
||||
"dereferenceable type: " +
|
||||
ty_to_str(tcx, oper_t));
|
||||
tcx.sess.span_fatal(
|
||||
expr.span,
|
||||
"dereferencing non-" +
|
||||
"dereferenceable type: " +
|
||||
istr::to_estr(ty_to_str(tcx, oper_t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::not. {
|
||||
if !type_is_integral(fcx, oper.span, oper_t) &&
|
||||
structure_of(fcx, oper.span, oper_t) != ty::ty_bool {
|
||||
tcx.sess.span_err(expr.span,
|
||||
#fmt["mismatched types: expected bool \
|
||||
or integer but found %s",
|
||||
ty_to_str(tcx, oper_t)]);
|
||||
tcx.sess.span_err(
|
||||
expr.span,
|
||||
#fmt["mismatched types: expected bool \
|
||||
or integer but found %s",
|
||||
istr::to_estr(ty_to_str(tcx, oper_t))]);
|
||||
}
|
||||
}
|
||||
ast::neg. {
|
||||
|
|
@ -1871,7 +1875,7 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
tcx.sess.span_err(expr.span,
|
||||
"applying unary minus to \
|
||||
non-numeric type "
|
||||
+ ty_to_str(tcx, oper_t));
|
||||
+ istr::to_estr(ty_to_str(tcx, oper_t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2004,7 +2008,7 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
tcx.sess.span_fatal(
|
||||
expr.span,
|
||||
"mismatched types: expected vector or string but "
|
||||
+ "found " + ty_to_str(tcx, ety));
|
||||
+ "found " + istr::to_estr(ty_to_str(tcx, ety)));
|
||||
}
|
||||
}
|
||||
bot |= check_for_or_for_each(fcx, decl, elt_ty, body, id);
|
||||
|
|
@ -2199,8 +2203,9 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
type_is_scalar(fcx, expr.span, t_1)) {
|
||||
tcx.sess.span_err(expr.span,
|
||||
"non-scalar cast: " +
|
||||
ty_to_str(tcx, expr_ty(tcx, e)) + " as " +
|
||||
ty_to_str(tcx, t_1));
|
||||
istr::to_estr(ty_to_str(tcx, expr_ty(tcx, e)))
|
||||
+ " as " +
|
||||
istr::to_estr(ty_to_str(tcx, t_1)));
|
||||
}
|
||||
write::ty_only_fixup(fcx, id, t_1);
|
||||
}
|
||||
|
|
@ -2297,7 +2302,7 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
let t_err = resolve_type_vars_if_possible(fcx, base_t);
|
||||
let msg =
|
||||
#fmt["attempted field access on type %s",
|
||||
ty_to_str(tcx, t_err)];
|
||||
istr::to_estr(ty_to_str(tcx, t_err))];
|
||||
tcx.sess.span_fatal(expr.span, msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -2312,7 +2317,7 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
tcx.sess.span_err(idx.span,
|
||||
"mismatched types: expected \
|
||||
integer but found "
|
||||
+ ty_to_str(tcx, idx_t));
|
||||
+ istr::to_estr(ty_to_str(tcx, idx_t)));
|
||||
}
|
||||
alt structure_of(fcx, expr.span, base_t) {
|
||||
ty::ty_vec(mt) { write::ty_only_fixup(fcx, id, mt.ty); }
|
||||
|
|
@ -2327,7 +2332,7 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
|||
_ {
|
||||
tcx.sess.span_fatal(expr.span,
|
||||
"vector-indexing bad type: " +
|
||||
ty_to_str(tcx, base_t));
|
||||
istr::to_estr(ty_to_str(tcx, base_t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2692,14 +2697,14 @@ fn check_main_fn_ty(tcx: &ty::ctxt, main_id: &ast::node_id) {
|
|||
let span = ast_map::node_span(tcx.items.get(main_id));
|
||||
tcx.sess.span_err(span,
|
||||
"wrong type in main function: found " +
|
||||
ty_to_str(tcx, main_t));
|
||||
istr::to_estr(ty_to_str(tcx, main_t)));
|
||||
}
|
||||
}
|
||||
_ {
|
||||
let span = ast_map::node_span(tcx.items.get(main_id));
|
||||
tcx.sess.span_bug(span,
|
||||
"main has a non-function type: found" +
|
||||
ty_to_str(tcx, main_t));
|
||||
istr::to_estr(ty_to_str(tcx, main_t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,12 +145,12 @@ fn lit_eq(l: &@ast::lit, m: &@ast::lit) -> bool {
|
|||
|
||||
tag call_kind { kind_call; kind_spawn; kind_bind; kind_for_each; }
|
||||
|
||||
fn call_kind_str(c: call_kind) -> str {
|
||||
fn call_kind_str(c: call_kind) -> istr {
|
||||
alt c {
|
||||
kind_call. { "Call" }
|
||||
kind_spawn. { "Spawn" }
|
||||
kind_bind. { "Bind" }
|
||||
kind_for_each. { "For-Each" }
|
||||
kind_call. { ~"Call" }
|
||||
kind_spawn. { ~"Spawn" }
|
||||
kind_bind. { ~"Bind" }
|
||||
kind_for_each. { ~"For-Each" }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,17 +160,17 @@ fn is_main_name(path: &[ast::ident]) -> bool {
|
|||
|
||||
// FIXME mode this to std::float when editing the stdlib no longer
|
||||
// requires a snapshot
|
||||
fn float_to_str(num: float, digits: uint) -> str {
|
||||
let accum = if num < 0.0 { num = -num; "-" } else { "" };
|
||||
fn float_to_str(num: float, digits: uint) -> istr {
|
||||
let accum = if num < 0.0 { num = -num; ~"-" } else { ~"" };
|
||||
let trunc = num as uint;
|
||||
let frac = num - (trunc as float);
|
||||
accum += istr::to_estr(uint::str(trunc));
|
||||
accum += uint::str(trunc);
|
||||
if frac == 0.0 || digits == 0u { ret accum; }
|
||||
accum += ".";
|
||||
accum += ~".";
|
||||
while digits > 0u && frac > 0.0 {
|
||||
frac *= 10.0;
|
||||
let digit = frac as uint;
|
||||
accum += istr::to_estr(uint::str(digit));
|
||||
accum += uint::str(digit);
|
||||
frac -= digit as float;
|
||||
digits -= 1u;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,117 +22,122 @@ import syntax::ast;
|
|||
import middle::ast_map;
|
||||
import metadata::csearch;
|
||||
|
||||
fn mode_str(m: &ty::mode) -> str {
|
||||
fn mode_str(m: &ty::mode) -> istr {
|
||||
alt m {
|
||||
mo_val. { "" }
|
||||
mo_alias(false) { "&" }
|
||||
mo_alias(true) { "&mutable " }
|
||||
mo_move. { "-" }
|
||||
mo_val. { ~"" }
|
||||
mo_alias(false) { ~"&" }
|
||||
mo_alias(true) { ~"&mutable " }
|
||||
mo_move. { ~"-" }
|
||||
}
|
||||
}
|
||||
|
||||
fn mode_str_1(m: &ty::mode) -> str {
|
||||
alt m { mo_val. { "val" } _ { mode_str(m) } }
|
||||
fn mode_str_1(m: &ty::mode) -> istr {
|
||||
alt m { mo_val. { ~"val" } _ { mode_str(m) } }
|
||||
}
|
||||
|
||||
fn fn_ident_to_string(id: ast::node_id, i: &ast::fn_ident) -> str {
|
||||
fn fn_ident_to_string(id: ast::node_id, i: &ast::fn_ident) -> istr {
|
||||
ret alt i {
|
||||
none. { istr::to_estr(~"anon" + int::str(id)) }
|
||||
some(s) { istr::to_estr(s) }
|
||||
none. { ~"anon" + int::str(id) }
|
||||
some(s) { s }
|
||||
};
|
||||
}
|
||||
|
||||
fn get_id_ident(cx: &ctxt, id: ast::def_id) -> str {
|
||||
fn get_id_ident(cx: &ctxt, id: ast::def_id) -> istr {
|
||||
if id.crate != ast::local_crate {
|
||||
str::connect(istr::to_estrs(cx.ext_map.get(id)), "::")
|
||||
istr::connect(cx.ext_map.get(id), ~"::")
|
||||
} else {
|
||||
alt cx.items.find(id.node) {
|
||||
some(ast_map::node_item(it)) { istr::to_estr(it.ident) }
|
||||
some(ast_map::node_item(it)) { it.ident }
|
||||
_ { fail "get_id_ident: can't find item in ast_map" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_to_str(cx: &ctxt, typ: &t) -> str {
|
||||
fn ty_to_str(cx: &ctxt, typ: &t) -> istr {
|
||||
fn fn_input_to_str(cx: &ctxt, input: &{mode: middle::ty::mode, ty: t}) ->
|
||||
str {
|
||||
istr {
|
||||
let s = mode_str(input.mode);
|
||||
ret s + ty_to_str(cx, input.ty);
|
||||
}
|
||||
fn fn_to_str(cx: &ctxt, proto: ast::proto, ident: option::t<ast::ident>,
|
||||
inputs: &[arg], output: t, cf: ast::controlflow,
|
||||
constrs: &[@constr]) -> str {
|
||||
let s = proto_to_str(proto);
|
||||
constrs: &[@constr]) -> istr {
|
||||
let s = istr::from_estr(proto_to_str(proto));
|
||||
alt ident {
|
||||
some(i) {
|
||||
s += " ";
|
||||
s += istr::to_estr(i);
|
||||
s += ~" ";
|
||||
s += i;
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
s += "(";
|
||||
s += ~"(";
|
||||
let strs = [];
|
||||
for a: arg in inputs { strs += [fn_input_to_str(cx, a)]; }
|
||||
s += str::connect(strs, ", ");
|
||||
s += ")";
|
||||
s += istr::connect(strs, ~", ");
|
||||
s += ~")";
|
||||
if struct(cx, output) != ty_nil {
|
||||
alt cf {
|
||||
ast::noreturn. { s += " -> !"; }
|
||||
ast::return. { s += " -> " + ty_to_str(cx, output); }
|
||||
ast::noreturn. { s += ~" -> !"; }
|
||||
ast::return. { s += ~" -> " + ty_to_str(cx, output); }
|
||||
}
|
||||
}
|
||||
s += constrs_str(constrs);
|
||||
ret s;
|
||||
}
|
||||
fn method_to_str(cx: &ctxt, m: &method) -> str {
|
||||
fn method_to_str(cx: &ctxt, m: &method) -> istr {
|
||||
ret fn_to_str(cx, m.proto, some::<ast::ident>(m.ident), m.inputs,
|
||||
m.output, m.cf, m.constrs) + ";";
|
||||
m.output, m.cf, m.constrs) + ~";";
|
||||
}
|
||||
fn field_to_str(cx: &ctxt, f: &field) -> str {
|
||||
ret istr::to_estr(f.ident) + ": " + mt_to_str(cx, f.mt);
|
||||
fn field_to_str(cx: &ctxt, f: &field) -> istr {
|
||||
ret f.ident + ~": " + mt_to_str(cx, f.mt);
|
||||
}
|
||||
fn mt_to_str(cx: &ctxt, m: &mt) -> str {
|
||||
fn mt_to_str(cx: &ctxt, m: &mt) -> istr {
|
||||
let mstr;
|
||||
alt m.mut {
|
||||
ast::mut. { mstr = "mutable "; }
|
||||
ast::imm. { mstr = ""; }
|
||||
ast::maybe_mut. { mstr = "mutable? "; }
|
||||
ast::mut. { mstr = ~"mutable "; }
|
||||
ast::imm. { mstr = ~""; }
|
||||
ast::maybe_mut. { mstr = ~"mutable? "; }
|
||||
}
|
||||
ret mstr + ty_to_str(cx, m.ty);
|
||||
}
|
||||
alt cname(cx, typ) { some(cs) { ret cs; } _ { } }
|
||||
alt cname(cx, typ) {
|
||||
some(cs) {
|
||||
ret istr::from_estr(cs);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
ret alt struct(cx, typ) {
|
||||
ty_native(_) { "native" }
|
||||
ty_nil. { "()" }
|
||||
ty_bot. { "_|_" }
|
||||
ty_bool. { "bool" }
|
||||
ty_int. { "int" }
|
||||
ty_float. { "float" }
|
||||
ty_uint. { "uint" }
|
||||
ty_machine(tm) { ty_mach_to_str(tm) }
|
||||
ty_char. { "char" }
|
||||
ty_str. { "str" }
|
||||
ty_istr. { "istr" }
|
||||
ty_box(tm) { "@" + mt_to_str(cx, tm) }
|
||||
ty_uniq(t) { "~" + ty_to_str(cx, t) }
|
||||
ty_vec(tm) { "[" + mt_to_str(cx, tm) + "]" }
|
||||
ty_type. { "type" }
|
||||
ty_native(_) { ~"native" }
|
||||
ty_nil. { ~"()" }
|
||||
ty_bot. { ~"_|_" }
|
||||
ty_bool. { ~"bool" }
|
||||
ty_int. { ~"int" }
|
||||
ty_float. { ~"float" }
|
||||
ty_uint. { ~"uint" }
|
||||
ty_machine(tm) { istr::from_estr(ty_mach_to_str(tm)) }
|
||||
ty_char. { ~"char" }
|
||||
ty_str. { ~"str" }
|
||||
ty_istr. { ~"istr" }
|
||||
ty_box(tm) { ~"@" + mt_to_str(cx, tm) }
|
||||
ty_uniq(t) { ~"~" + ty_to_str(cx, t) }
|
||||
ty_vec(tm) { ~"[" + mt_to_str(cx, tm) + ~"]" }
|
||||
ty_type. { ~"type" }
|
||||
ty_rec(elems) {
|
||||
let strs: [str] = [];
|
||||
let strs: [istr] = [];
|
||||
for fld: field in elems { strs += [field_to_str(cx, fld)]; }
|
||||
"{" + str::connect(strs, ",") + "}"
|
||||
~"{" + istr::connect(strs, ~",") + ~"}"
|
||||
}
|
||||
ty_tup(elems) {
|
||||
let strs = [];
|
||||
for elem in elems { strs += [ty_to_str(cx, elem)]; }
|
||||
"(" + str::connect(strs, ",") + ")"
|
||||
~"(" + istr::connect(strs, ~",") + ~")"
|
||||
}
|
||||
ty_tag(id, tps) {
|
||||
let s = get_id_ident(cx, id);
|
||||
if vec::len::<t>(tps) > 0u {
|
||||
let strs: [str] = [];
|
||||
let strs: [istr] = [];
|
||||
for typ: t in tps { strs += [ty_to_str(cx, typ)]; }
|
||||
s += "[" + str::connect(strs, ",") + "]";
|
||||
s += ~"[" + istr::connect(strs, ~",") + ~"]";
|
||||
}
|
||||
s
|
||||
}
|
||||
|
|
@ -146,14 +151,14 @@ fn ty_to_str(cx: &ctxt, typ: &t) -> str {
|
|||
ty_obj(meths) {
|
||||
let strs = [];
|
||||
for m: method in meths { strs += [method_to_str(cx, m)]; }
|
||||
"obj {\n\t" + str::connect(strs, "\n\t") + "\n}"
|
||||
~"obj {\n\t" + istr::connect(strs, ~"\n\t") + ~"\n}"
|
||||
}
|
||||
ty_res(id, _, _) { get_id_ident(cx, id) }
|
||||
ty_var(v) { istr::to_estr(~"<T" + int::str(v) + ~">") }
|
||||
ty_var(v) { ~"<T" + int::str(v) + ~">" }
|
||||
ty_param(id, _) {
|
||||
"'" + str::unsafe_from_bytes([('a' as u8) + (id as u8)])
|
||||
~"'" + istr::unsafe_from_bytes([('a' as u8) + (id as u8)])
|
||||
}
|
||||
_ { istr::to_estr(ty_to_short_str(cx, typ)) }
|
||||
_ { ty_to_short_str(cx, typ) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,25 +168,27 @@ fn ty_to_short_str(cx: &ctxt, typ: t) -> istr {
|
|||
ret s;
|
||||
}
|
||||
|
||||
fn constr_to_str(c: &@constr) -> str {
|
||||
ret path_to_str(c.node.path) +
|
||||
pprust::constr_args_to_str(pprust::uint_to_str, c.node.args);
|
||||
fn constr_to_str(c: &@constr) -> istr {
|
||||
ret istr::from_estr(path_to_str(c.node.path)) +
|
||||
istr::from_estr(
|
||||
pprust::constr_args_to_str(pprust::uint_to_str, c.node.args));
|
||||
}
|
||||
|
||||
fn constrs_str(constrs: &[@constr]) -> str {
|
||||
let s = "";
|
||||
fn constrs_str(constrs: &[@constr]) -> istr {
|
||||
let s = ~"";
|
||||
let colon = true;
|
||||
for c: @constr in constrs {
|
||||
if colon { s += " : "; colon = false; } else { s += ", "; }
|
||||
if colon { s += ~" : "; colon = false; } else { s += ~", "; }
|
||||
s += constr_to_str(c);
|
||||
}
|
||||
ret s;
|
||||
}
|
||||
|
||||
fn ty_constr_to_str<Q>(c: &@ast::spanned<ast::constr_general_<ast::path, Q>>)
|
||||
-> str {
|
||||
ret path_to_str(c.node.path) +
|
||||
constr_args_to_str::<ast::path>(path_to_str, c.node.args);
|
||||
-> istr {
|
||||
ret istr::from_estr(path_to_str(c.node.path)) +
|
||||
istr::from_estr(
|
||||
constr_args_to_str::<ast::path>(path_to_str, c.node.args));
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue