From 9a38ed12ca37c7147a973824f70c1b909c9c686c Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 24 Dec 2021 14:31:17 -0500 Subject: [PATCH 1/4] Rename args to check_argument_types and add some comments for what they are --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 83 ++++++++++--------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 11b63a99043b7..3ef939df755fd 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -96,34 +96,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// method calls and overloaded operators. pub(in super::super) fn check_argument_types( &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - fn_inputs: &[Ty<'tcx>], - expected_arg_tys: &[Ty<'tcx>], - args: &'tcx [hir::Expr<'tcx>], + // Span enclosing the call site + call_span: Span, + // Expression of the call site + call_expr: &'tcx hir::Expr<'tcx>, + // Types (as defined in the *signature* of the target function) + formal_input_tys: &[Ty<'tcx>], + // More specific expected types, after unifying with caller output types + expected_input_tys: &[Ty<'tcx>], + // The expressions for each provided argument + provided_args: &'tcx [hir::Expr<'tcx>], + // Whether the function is variadic, for example when imported from C c_variadic: bool, + // Whether the arguments have been bundled in a tuple (ex: closures) tuple_arguments: TupleArgumentsFlag, - def_id: Option, + // The DefId for the function being called, for better error messages + fn_def_id: Option, ) { let tcx = self.tcx; // Grab the argument types, supplying fresh type variables // if the wrong number of arguments were supplied - let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 }; + let supplied_arg_count = + if tuple_arguments == DontTupleArguments { provided_args.len() } else { 1 }; // All the input types from the fn signature must outlive the call // so as to validate implied bounds. - for (&fn_input_ty, arg_expr) in iter::zip(fn_inputs, args) { + for (&fn_input_ty, arg_expr) in iter::zip(formal_input_tys, provided_args) { self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); } - let expected_arg_count = fn_inputs.len(); + let expected_arg_count = formal_input_tys.len(); let param_count_error = |expected_count: usize, arg_count: usize, error_code: &str, c_variadic: bool, sugg_unit: bool| { - let (span, start_span, args, ctor_of) = match &expr.kind { + let (span, start_span, args, ctor_of) = match &call_expr.kind { hir::ExprKind::Call( hir::Expr { span, @@ -156,14 +165,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &args[1..], // Skip the receiver. None, // methods are never ctors ), - k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k), + k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), }; - let arg_spans = if args.is_empty() { + let arg_spans = if provided_args.is_empty() { // foo() // ^^^-- supplied 0 arguments // | // expected 2 arguments - vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())] + vec![tcx.sess.source_map().next_point(start_span).with_hi(call_span.hi())] } else { // foo(1, 2, 3) // ^^^ - - - supplied 3 arguments @@ -196,7 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if let Some(def_id) = def_id { + if let Some(def_id) = fn_def_id { if let Some(def_span) = tcx.def_ident_span(def_id) { let mut spans: MultiSpan = def_span.into(); @@ -218,7 +227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if sugg_unit { - let sugg_span = tcx.sess.source_map().end_point(expr.span); + let sugg_span = tcx.sess.source_map().end_point(call_expr.span); // remove closing `)` from the span let sugg_span = sugg_span.shrink_to_lo(); err.span_suggestion( @@ -240,15 +249,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); }; - let mut expected_arg_tys = expected_arg_tys.to_vec(); + let mut expected_arg_tys = expected_input_tys.to_vec(); let formal_tys = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); + let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]); match tuple_type.kind() { - ty::Tuple(arg_types) if arg_types.len() != args.len() => { - param_count_error(arg_types.len(), args.len(), "E0057", false, false); + ty::Tuple(arg_types) if arg_types.len() != provided_args.len() => { + param_count_error(arg_types.len(), provided_args.len(), "E0057", false, false); expected_arg_tys = vec![]; - self.err_args(args.len()) + self.err_args(provided_args.len()) } ty::Tuple(arg_types) => { expected_arg_tys = match expected_arg_tys.get(0) { @@ -263,21 +272,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { struct_span_err!( tcx.sess, - sp, + call_span, E0059, "cannot use call notation; the first type parameter \ for the function trait is neither a tuple nor unit" ) .emit(); expected_arg_tys = vec![]; - self.err_args(args.len()) + self.err_args(provided_args.len()) } } } else if expected_arg_count == supplied_arg_count { - fn_inputs.to_vec() + formal_input_tys.to_vec() } else if c_variadic { if supplied_arg_count >= expected_arg_count { - fn_inputs.to_vec() + formal_input_tys.to_vec() } else { param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); expected_arg_tys = vec![]; @@ -287,8 +296,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // is the missing argument of type `()`? let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { self.resolve_vars_if_possible(expected_arg_tys[0]).is_unit() - } else if fn_inputs.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(fn_inputs[0]).is_unit() + } else if formal_input_tys.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(formal_input_tys[0]).is_unit() } else { false }; @@ -322,13 +331,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the call. This helps coercions. if check_closures { self.select_obligations_where_possible(false, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, expr); + self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); self.point_at_arg_instead_of_call_if_possible( errors, &final_arg_types, - expr, - sp, - &args, + call_expr, + call_span, + &provided_args, ); }) } @@ -339,11 +348,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let t = if c_variadic { expected_arg_count } else if tuple_arguments == TupleArguments { - args.len() + provided_args.len() } else { supplied_arg_count }; - for (i, arg) in args.iter().take(t).enumerate() { + for (i, arg) in provided_args.iter().take(t).enumerate() { // Warn only for the first loop (the "no closures" one). // Closure arguments themselves can't be diverging, but // a previous argument can, e.g., `foo(panic!(), || {})`. @@ -380,13 +389,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let _ = self.resolve_vars_with_obligations_and_mutate_fulfillment( coerce_ty, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, expr); + self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); self.point_at_arg_instead_of_call_if_possible( errors, &final_arg_types, - expr, - sp, - args, + call_expr, + call_span, + provided_args, ); }, ); @@ -410,7 +419,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit() } - for arg in args.iter().skip(expected_arg_count) { + for arg in provided_args.iter().skip(expected_arg_count) { let arg_ty = self.check_expr(&arg); // There are a few types which get autopromoted when passed via varargs From 45341a6cbf07e93960feede6f78a0a3f441d6d89 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 24 Dec 2021 17:15:52 -0500 Subject: [PATCH 2/4] Rename a couple variables --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 3ef939df755fd..1204f1979f02a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = method.unwrap(); // HACK(eddyb) ignore self in the definition (see above). - let expected_arg_tys = self.expected_inputs_for_expected_output( + let expected_input_tys = self.expected_inputs_for_expected_output( sp, expected, method.sig.output(), @@ -83,7 +83,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp, expr, &method.sig.inputs()[1..], - &expected_arg_tys[..], + &expected_input_tys[..], args_no_rcvr, method.sig.c_variadic, tuple_arguments, @@ -249,18 +249,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); }; - let mut expected_arg_tys = expected_input_tys.to_vec(); + let mut expected_input_tys = expected_input_tys.to_vec(); - let formal_tys = if tuple_arguments == TupleArguments { + let formal_input_tys = if tuple_arguments == TupleArguments { let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]); match tuple_type.kind() { ty::Tuple(arg_types) if arg_types.len() != provided_args.len() => { param_count_error(arg_types.len(), provided_args.len(), "E0057", false, false); - expected_arg_tys = vec![]; + expected_input_tys = vec![]; self.err_args(provided_args.len()) } ty::Tuple(arg_types) => { - expected_arg_tys = match expected_arg_tys.get(0) { + expected_input_tys = match expected_input_tys.get(0) { Some(&ty) => match ty.kind() { ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), _ => vec![], @@ -278,7 +278,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for the function trait is neither a tuple nor unit" ) .emit(); - expected_arg_tys = vec![]; + expected_input_tys = vec![]; self.err_args(provided_args.len()) } } @@ -289,13 +289,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys.to_vec() } else { param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); - expected_arg_tys = vec![]; + expected_input_tys = vec![]; self.err_args(supplied_arg_count) } } else { // is the missing argument of type `()`? - let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(expected_arg_tys[0]).is_unit() + let sugg_unit = if expected_input_tys.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(expected_input_tys[0]).is_unit() } else if formal_input_tys.len() == 1 && supplied_arg_count == 0 { self.resolve_vars_if_possible(formal_input_tys[0]).is_unit() } else { @@ -303,18 +303,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); - expected_arg_tys = vec![]; + expected_input_tys = vec![]; self.err_args(supplied_arg_count) }; debug!( - "check_argument_types: formal_tys={:?}", - formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::>() + "check_argument_types: formal_input_tys={:?}", + formal_input_tys.iter().map(|t| self.ty_to_string(*t)).collect::>() ); - // If there is no expectation, expect formal_tys. - let expected_arg_tys = - if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() }; + // If there is no expectation, expect formal_input_tys. + let expected_input_tys = if !expected_input_tys.is_empty() { + expected_input_tys + } else { + formal_input_tys.clone() + }; let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; @@ -366,12 +369,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - let formal_ty = formal_tys[i]; + let formal_ty = formal_input_tys[i]; debug!("checking argument {}: {:?} = {:?}", i, arg, formal_ty); // The special-cased logic below has three functions: // 1. Provide as good of an expected type as possible. - let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); + let expected = Expectation::rvalue_hint(self, expected_input_tys[i]); let checked_ty = self.check_expr_with_expectation(&arg, expected); From 555119fa146640c24104e79ec32beeec9d5f738a Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 24 Dec 2021 23:34:26 -0500 Subject: [PATCH 3/4] Introduce demand_compatible --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 91 ++++++++++--------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 1204f1979f02a..7664c52a34105 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -321,6 +321,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; + // We introduce a helper function to demand that a given argument satisfy a given input + // This is more complicated than just checking type equality, as arguments could be coerced + // This version writes those types back so further type checking uses the narrowed types + let demand_compatible = |idx, final_arg_types: &mut Vec<(usize, Ty<'tcx>, Ty<'tcx>)>| { + let formal_input_ty: Ty<'tcx> = formal_input_tys[idx]; + let expected_input_ty: Ty<'tcx> = expected_input_tys[idx]; + let provided_arg = &provided_args[idx]; + + debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); + + // The special-cased logic below has three functions: + // 1. Provide as good of an expected type as possible. + let expectation = Expectation::rvalue_hint(self, expected_input_ty); + + let checked_ty = self.check_expr_with_expectation(provided_arg, expectation); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); + + // Keep track of these for below + final_arg_types.push((idx, checked_ty, coerced_ty)); + + // Cause selection errors caused by resolving a single argument to point at the + // argument and not the call. This is otherwise redundant with the `demand_coerce` + // call immediately after, but it lets us customize the span pointed to in the + // fulfillment error to be more accurate. + let _ = + self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| { + self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); + self.point_at_arg_instead_of_call_if_possible( + errors, + &final_arg_types, + call_expr, + call_span, + provided_args, + ); + }); + + // We're processing function arguments so we definitely want to use + // two-phase borrows. + self.demand_coerce(&provided_arg, checked_ty, coerced_ty, None, AllowTwoPhase::Yes); + + // 3. Relate the expected type and the formal one, + // if the expected type was used for the coercion. + self.demand_suptype(provided_arg.span, formal_input_ty, coerced_ty); + }; + // Check the arguments. // We do this in a pretty awful way: first we type-check any arguments // that are not closures, then we type-check the closures. This is so @@ -369,47 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - let formal_ty = formal_input_tys[i]; - debug!("checking argument {}: {:?} = {:?}", i, arg, formal_ty); - - // The special-cased logic below has three functions: - // 1. Provide as good of an expected type as possible. - let expected = Expectation::rvalue_hint(self, expected_input_tys[i]); - - let checked_ty = self.check_expr_with_expectation(&arg, expected); - - // 2. Coerce to the most detailed type that could be coerced - // to, which is `expected_ty` if `rvalue_hint` returns an - // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); - - final_arg_types.push((i, checked_ty, coerce_ty)); - - // Cause selection errors caused by resolving a single argument to point at the - // argument and not the call. This is otherwise redundant with the `demand_coerce` - // call immediately after, but it lets us customize the span pointed to in the - // fulfillment error to be more accurate. - let _ = self.resolve_vars_with_obligations_and_mutate_fulfillment( - coerce_ty, - |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); - self.point_at_arg_instead_of_call_if_possible( - errors, - &final_arg_types, - call_expr, - call_span, - provided_args, - ); - }, - ); - - // We're processing function arguments so we definitely want to use - // two-phase borrows. - self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes); - - // 3. Relate the expected type and the formal one, - // if the expected type was used for the coercion. - self.demand_suptype(arg.span, formal_ty, coerce_ty); + demand_compatible(i, &mut final_arg_types); } } From bbb8bde98939db872e2b6092c0bf0c97f23e9da2 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Mon, 27 Dec 2021 23:18:17 -0500 Subject: [PATCH 4/4] Slight cleanup --- compiler/rustc_typeck/src/check/callee.rs | 4 +- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 60 +++++++++---------- .../variadic-unreachable-arg-error.rs | 14 +++++ 3 files changed, 43 insertions(+), 35 deletions(-) create mode 100644 src/test/ui/c-variadic/variadic-unreachable-arg-error.rs diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index e67ee1cab3df2..eea8f40635d74 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -496,7 +496,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr.span, call_expr, fn_sig.inputs(), - &expected_arg_tys, + expected_arg_tys, arg_exprs, fn_sig.c_variadic, TupleArgumentsFlag::DontTupleArguments, @@ -529,7 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr.span, call_expr, fn_sig.inputs(), - &expected_arg_tys, + expected_arg_tys, arg_exprs, fn_sig.c_variadic, TupleArgumentsFlag::TupleArguments, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 7664c52a34105..e796fe58170d2 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -62,7 +62,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp, expr, &err_inputs, - &[], + vec![], args_no_rcvr, false, tuple_arguments, @@ -83,7 +83,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp, expr, &method.sig.inputs()[1..], - &expected_input_tys[..], + expected_input_tys, args_no_rcvr, method.sig.c_variadic, tuple_arguments, @@ -103,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Types (as defined in the *signature* of the target function) formal_input_tys: &[Ty<'tcx>], // More specific expected types, after unifying with caller output types - expected_input_tys: &[Ty<'tcx>], + expected_input_tys: Vec>, // The expressions for each provided argument provided_args: &'tcx [hir::Expr<'tcx>], // Whether the function is variadic, for example when imported from C @@ -249,25 +249,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); }; - let mut expected_input_tys = expected_input_tys.to_vec(); - - let formal_input_tys = if tuple_arguments == TupleArguments { + let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]); match tuple_type.kind() { ty::Tuple(arg_types) if arg_types.len() != provided_args.len() => { param_count_error(arg_types.len(), provided_args.len(), "E0057", false, false); - expected_input_tys = vec![]; - self.err_args(provided_args.len()) + (self.err_args(provided_args.len()), vec![]) } ty::Tuple(arg_types) => { - expected_input_tys = match expected_input_tys.get(0) { + let expected_input_tys = match expected_input_tys.get(0) { Some(&ty) => match ty.kind() { ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), _ => vec![], }, None => vec![], }; - arg_types.iter().map(|k| k.expect_ty()).collect() + (arg_types.iter().map(|k| k.expect_ty()).collect(), expected_input_tys) } _ => { struct_span_err!( @@ -278,19 +275,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for the function trait is neither a tuple nor unit" ) .emit(); - expected_input_tys = vec![]; - self.err_args(provided_args.len()) + (self.err_args(provided_args.len()), vec![]) } } } else if expected_arg_count == supplied_arg_count { - formal_input_tys.to_vec() + (formal_input_tys.to_vec(), expected_input_tys) } else if c_variadic { if supplied_arg_count >= expected_arg_count { - formal_input_tys.to_vec() + (formal_input_tys.to_vec(), expected_input_tys) } else { param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); - expected_input_tys = vec![]; - self.err_args(supplied_arg_count) + (self.err_args(supplied_arg_count), vec![]) } } else { // is the missing argument of type `()`? @@ -303,8 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); - expected_input_tys = vec![]; - self.err_args(supplied_arg_count) + (self.err_args(supplied_arg_count), vec![]) }; debug!( @@ -319,6 +313,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys.clone() }; + assert_eq!(expected_input_tys.len(), formal_input_tys.len()); + + // Keep track of the fully coerced argument types let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; // We introduce a helper function to demand that a given argument satisfy a given input @@ -376,8 +373,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that we have more information about the types of arguments when we // type-check the functions. This isn't really the right way to do this. for check_closures in [false, true] { - debug!("check_closures={}", check_closures); - // More awful hacks: before we check argument types, try to do // an "opportunistic" trait resolution of any trait bounds on // the call. This helps coercions. @@ -394,17 +389,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } - // For C-variadic functions, we don't have a declared type for all of - // the arguments hence we only do our usual type checking with - // the arguments who's types we do know. - let t = if c_variadic { - expected_arg_count - } else if tuple_arguments == TupleArguments { - provided_args.len() - } else { - supplied_arg_count - }; - for (i, arg) in provided_args.iter().take(t).enumerate() { + let minimum_input_count = formal_input_tys.len(); + for (idx, arg) in provided_args.iter().enumerate() { // Warn only for the first loop (the "no closures" one). // Closure arguments themselves can't be diverging, but // a previous argument can, e.g., `foo(panic!(), || {})`. @@ -412,13 +398,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); } - let is_closure = matches!(arg.kind, ExprKind::Closure(..)); + // For C-variadic functions, we don't have a declared type for all of + // the arguments hence we only do our usual type checking with + // the arguments who's types we do know. However, we *can* check + // for unreachable expressions (see above). + // FIXME: unreachable warning current isn't emitted + if idx >= minimum_input_count { + continue; + } + let is_closure = matches!(arg.kind, ExprKind::Closure(..)); if is_closure != check_closures { continue; } - demand_compatible(i, &mut final_arg_types); + demand_compatible(idx, &mut final_arg_types); } } diff --git a/src/test/ui/c-variadic/variadic-unreachable-arg-error.rs b/src/test/ui/c-variadic/variadic-unreachable-arg-error.rs new file mode 100644 index 0000000000000..f60f6f3e80872 --- /dev/null +++ b/src/test/ui/c-variadic/variadic-unreachable-arg-error.rs @@ -0,0 +1,14 @@ +// check-pass + +#![feature(c_variadic)] + +extern "C" { + fn foo(f: isize, x: u8, ...); +} + +fn main() { + unsafe { + // FIXME: Ideally we could give an unreachable warning + foo(1, loop {}, 1usize); + } +}