Skip to content

Commit

Permalink
Check that fn arguments are WF when calling the fn. In combination with
Browse files Browse the repository at this point in the history
invariance, this restores soundness to implied bounds (I think). :)

Fixes rust-lang#25860.
  • Loading branch information
nikomatsakis committed Jun 9, 2015
1 parent 8284ecd commit 291474f
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 13 deletions.
4 changes: 4 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,10 @@ pub struct UpvarBorrow {
pub type UpvarCaptureMap = FnvHashMap<UpvarId, UpvarCapture>;

impl Region {
pub fn from_node_id(node_id: ast::NodeId) -> Region {
ReScope(region::CodeExtent::from_node_id(node_id))
}

pub fn is_bound(&self) -> bool {
match *self {
ty::ReEarlyBound(..) => true,
Expand Down
10 changes: 5 additions & 5 deletions src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}

Some(CallStep::Overloaded(method_callee)) => {
confirm_overloaded_call(fcx, call_expr, callee_expr,
arg_exprs, expected, method_callee);
confirm_overloaded_call(fcx, call_expr, arg_exprs, expected, method_callee);
}
}
}
Expand Down Expand Up @@ -263,6 +262,7 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
&fn_sig.inputs);
check_argument_types(fcx,
call_expr.span,
ty::Region::from_node_id(call_expr.id),
&fn_sig.inputs,
&expected_arg_tys[..],
arg_exprs,
Expand Down Expand Up @@ -292,6 +292,7 @@ fn confirm_deferred_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,

check_argument_types(fcx,
call_expr.span,
ty::Region::from_node_id(call_expr.id),
&*fn_sig.inputs,
&*expected_arg_tys,
arg_exprs,
Expand All @@ -302,17 +303,16 @@ fn confirm_deferred_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
}

fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &'tcx ast::Expr,
call_expr: &'tcx ast::Expr,
arg_exprs: &'tcx [P<ast::Expr>],
expected: Expectation<'tcx>,
method_callee: ty::MethodCallee<'tcx>)
{
let output_type =
check_method_argument_types(fcx,
call_expr.span,
call_expr,
method_callee.ty,
callee_expr,
arg_exprs,
TupleArgumentsFlag::TupleArguments,
expected);
Expand Down
25 changes: 17 additions & 8 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2178,9 +2178,9 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}

fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
sp: Span,
span: Span,
call_expr: &'tcx ast::Expr,
method_fn_ty: Ty<'tcx>,
callee_expr: &'tcx ast::Expr,
args_no_rcvr: &'tcx [P<ast::Expr>],
tuple_arguments: TupleArgumentsFlag,
expected: Expectation<'tcx>)
Expand All @@ -2194,7 +2194,8 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
};

check_argument_types(fcx,
sp,
span,
ty::Region::from_node_id(call_expr.id),
&err_inputs[..],
&[],
args_no_rcvr,
Expand All @@ -2206,12 +2207,13 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
ty::ty_bare_fn(_, ref fty) => {
// HACK(eddyb) ignore self in the definition (see above).
let expected_arg_tys = expected_types_for_fn_args(fcx,
sp,
span,
expected,
fty.sig.0.output,
&fty.sig.0.inputs[1..]);
check_argument_types(fcx,
sp,
span,
ty::Region::from_node_id(call_expr.id),
&fty.sig.0.inputs[1..],
&expected_arg_tys[..],
args_no_rcvr,
Expand All @@ -2220,8 +2222,7 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
fty.sig.0.output
}
_ => {
fcx.tcx().sess.span_bug(callee_expr.span,
"method without bare fn type");
fcx.tcx().sess.span_bug(span, "method without bare fn type");
}
}
}
Expand All @@ -2231,13 +2232,21 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
/// operators.
fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
sp: Span,
call_scope: ty::Region,
fn_inputs: &[Ty<'tcx>],
expected_arg_tys: &[Ty<'tcx>],
args: &'tcx [P<ast::Expr>],
variadic: bool,
tuple_arguments: TupleArgumentsFlag) {
let tcx = fcx.ccx.tcx;

// All the input types from the fn signature must outlive the call
// so as to validate implied bounds.
for &fn_input_ty in fn_inputs {
let cause = traits::ObligationCause::misc(sp, fcx.body_id);
fcx.register_region_obligation(fn_input_ty, call_scope, cause);
}

// Grab the argument types, supplying fresh type variables
// if the wrong number of arguments were supplied
let supplied_arg_count = if tuple_arguments == DontTupleArguments {
Expand Down Expand Up @@ -2665,8 +2674,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
// Call the generic checker.
let ret_ty = check_method_argument_types(fcx,
method_name.span,
fn_ty,
expr,
fn_ty,
&args[1..],
DontTupleArguments,
expected);
Expand Down
21 changes: 21 additions & 0 deletions src/test/compile-fail/issue-25860-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2015 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.

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
let f: fn(&'static &'static (), &'a T) -> &'static T = foo;
//~^ ERROR cannot infer
f(UNIT, x)
}

fn main( ) { }
20 changes: 20 additions & 0 deletions src/test/compile-fail/issue-25860-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2015 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.

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
foo(UNIT, x)
//~^ ERROR cannot infer
}

fn main() { }
24 changes: 24 additions & 0 deletions src/test/compile-fail/issue-25860-3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2015 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.

static UNIT: &'static &'static () = &&();

trait Foo: Sized {
fn foo<'a,'b,T>(self, _: &'a &'b (), v: &'b T) -> &'a T { v }
}

impl Foo for () { }

fn bad<'a, T>(x: &'a T) -> &'static T {
().foo(UNIT, x)
//~^ ERROR cannot infer
}

fn main() { }

0 comments on commit 291474f

Please sign in to comment.