Note inference failures using ?
conversion
This commit is contained in:
parent
c97f11af7b
commit
0496fdee4f
10 changed files with 239 additions and 44 deletions
|
@ -69,7 +69,7 @@ use rustc_middle::ty::{
|
||||||
subst::{Subst, SubstsRef},
|
subst::{Subst, SubstsRef},
|
||||||
Region, Ty, TyCtxt, TypeFoldable,
|
Region, Ty, TyCtxt, TypeFoldable,
|
||||||
};
|
};
|
||||||
use rustc_span::{BytePos, DesugaringKind, Pos, Span};
|
use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
|
||||||
use rustc_target::spec::abi;
|
use rustc_target::spec::abi;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::{cmp, fmt};
|
use std::{cmp, fmt};
|
||||||
|
@ -2282,6 +2282,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
self.note_region_origin(&mut err, &sub_origin);
|
self.note_region_origin(&mut err, &sub_origin);
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine whether an error associated with the given span and definition
|
||||||
|
/// should be treated as being caused by the implicit `From` conversion
|
||||||
|
/// within `?` desugaring.
|
||||||
|
pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool {
|
||||||
|
span.is_desugaring(DesugaringKind::QuestionMark)
|
||||||
|
&& self.tcx.is_diagnostic_item(sym::from_trait, trait_def_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::infer::InferCtxt;
|
||||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
|
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Namespace};
|
use rustc_hir::def::{DefKind, Namespace};
|
||||||
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat};
|
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat};
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
|
@ -25,6 +26,7 @@ struct FindHirNodeVisitor<'a, 'tcx> {
|
||||||
found_closure: Option<&'tcx Expr<'tcx>>,
|
found_closure: Option<&'tcx Expr<'tcx>>,
|
||||||
found_method_call: Option<&'tcx Expr<'tcx>>,
|
found_method_call: Option<&'tcx Expr<'tcx>>,
|
||||||
found_exact_method_call: Option<&'tcx Expr<'tcx>>,
|
found_exact_method_call: Option<&'tcx Expr<'tcx>>,
|
||||||
|
found_use_diagnostic: Option<UseDiagnostic<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
|
||||||
|
@ -39,15 +41,16 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
|
||||||
found_closure: None,
|
found_closure: None,
|
||||||
found_method_call: None,
|
found_method_call: None,
|
||||||
found_exact_method_call: None,
|
found_exact_method_call: None,
|
||||||
|
found_use_diagnostic: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_ty_contains_target(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> {
|
fn node_type_opt(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
|
||||||
self.infcx
|
self.infcx.in_progress_typeck_results?.borrow().node_type_opt(hir_id)
|
||||||
.in_progress_typeck_results
|
}
|
||||||
.and_then(|typeck_results| typeck_results.borrow().node_type_opt(hir_id))
|
|
||||||
.map(|ty| self.infcx.resolve_vars_if_possible(ty))
|
fn node_ty_contains_target(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
|
||||||
.filter(|ty| {
|
self.node_type_opt(hir_id).map(|ty| self.infcx.resolve_vars_if_possible(ty)).filter(|ty| {
|
||||||
ty.walk().any(|inner| {
|
ty.walk().any(|inner| {
|
||||||
inner == self.target
|
inner == self.target
|
||||||
|| match (inner.unpack(), self.target.unpack()) {
|
|| match (inner.unpack(), self.target.unpack()) {
|
||||||
|
@ -68,6 +71,14 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine whether the expression, assumed to be the callee within a `Call`,
|
||||||
|
/// corresponds to the `From::from` emitted in desugaring of the `?` operator.
|
||||||
|
fn is_try_conversion(&self, callee: &Expr<'tcx>) -> bool {
|
||||||
|
self.infcx
|
||||||
|
.trait_def_from_hir_fn(callee.hir_id)
|
||||||
|
.map_or(false, |def_id| self.infcx.is_try_conversion(callee.span, def_id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
|
||||||
|
@ -119,10 +130,23 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
|
||||||
// are handled specially, but instead they should be handled in `annotate_method_call`,
|
// are handled specially, but instead they should be handled in `annotate_method_call`,
|
||||||
// which currently doesn't work because this evaluates to `false` for const arguments.
|
// which currently doesn't work because this evaluates to `false` for const arguments.
|
||||||
// See https://github.com/rust-lang/rust/pull/77758 for more details.
|
// See https://github.com/rust-lang/rust/pull/77758 for more details.
|
||||||
if self.node_ty_contains_target(expr.hir_id).is_some() {
|
if let Some(ty) = self.node_ty_contains_target(expr.hir_id) {
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ExprKind::Closure(..) => self.found_closure = Some(&expr),
|
ExprKind::Closure(..) => self.found_closure = Some(&expr),
|
||||||
ExprKind::MethodCall(..) => self.found_method_call = Some(&expr),
|
ExprKind::MethodCall(..) => self.found_method_call = Some(&expr),
|
||||||
|
|
||||||
|
// If the given expression falls within the target span and is a
|
||||||
|
// `From::from(e)` call emitted during desugaring of the `?` operator,
|
||||||
|
// extract the types inferred before and after the call
|
||||||
|
ExprKind::Call(callee, [arg])
|
||||||
|
if self.target_span.contains(expr.span)
|
||||||
|
&& self.found_use_diagnostic.is_none()
|
||||||
|
&& self.is_try_conversion(callee) =>
|
||||||
|
{
|
||||||
|
self.found_use_diagnostic = self.node_type_opt(arg.hir_id).map(|pre_ty| {
|
||||||
|
UseDiagnostic::TryConversion { pre_ty, post_ty: ty, span: callee.span }
|
||||||
|
});
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +154,67 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An observation about the use site of a type to be emitted as an additional
|
||||||
|
/// note in an inference failure error.
|
||||||
|
enum UseDiagnostic<'tcx> {
|
||||||
|
/// Records the types inferred before and after `From::from` is called on the
|
||||||
|
/// error value within the desugaring of the `?` operator.
|
||||||
|
TryConversion { pre_ty: Ty<'tcx>, post_ty: Ty<'tcx>, span: Span },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UseDiagnostic<'_> {
|
||||||
|
/// Return a descriptor of the value at the use site
|
||||||
|
fn descr(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::TryConversion { .. } => "`?` error",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a descriptor of the type at the use site
|
||||||
|
fn type_descr(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::TryConversion { .. } => "`?` error type",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn applies_to(&self, span: Span) -> bool {
|
||||||
|
match *self {
|
||||||
|
// In some cases the span for an inference failure due to try
|
||||||
|
// conversion contains the antecedent expression as well as the `?`
|
||||||
|
Self::TryConversion { span: s, .. } => span.contains(s) && span.hi() == s.hi(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attach_note(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||||
|
match *self {
|
||||||
|
Self::TryConversion { pre_ty, post_ty, .. } => {
|
||||||
|
let pre_ty = pre_ty.to_string();
|
||||||
|
let post_ty = post_ty.to_string();
|
||||||
|
|
||||||
|
let intro = "the `?` operation implicitly converts the error value";
|
||||||
|
|
||||||
|
let msg = match (pre_ty.as_str(), post_ty.as_str()) {
|
||||||
|
("_", "_") => format!("{} using the `From` trait", intro),
|
||||||
|
(_, "_") => {
|
||||||
|
format!("{} into a type implementing `From<{}>`", intro, pre_ty)
|
||||||
|
}
|
||||||
|
("_", _) => {
|
||||||
|
format!("{} into `{}` using the `From` trait", intro, post_ty)
|
||||||
|
}
|
||||||
|
(_, _) => {
|
||||||
|
format!(
|
||||||
|
"{} into `{}` using its implementation of `From<{}>`",
|
||||||
|
intro, post_ty, pre_ty
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
err.note(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Suggest giving an appropriate return type to a closure expression.
|
/// Suggest giving an appropriate return type to a closure expression.
|
||||||
fn closure_return_type_suggestion(
|
fn closure_return_type_suggestion(
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -139,6 +224,7 @@ fn closure_return_type_suggestion(
|
||||||
descr: &str,
|
descr: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
ret: &str,
|
ret: &str,
|
||||||
|
use_diag: Option<&UseDiagnostic<'_>>,
|
||||||
parent_name: Option<String>,
|
parent_name: Option<String>,
|
||||||
parent_descr: Option<&str>,
|
parent_descr: Option<&str>,
|
||||||
) {
|
) {
|
||||||
|
@ -160,7 +246,15 @@ fn closure_return_type_suggestion(
|
||||||
);
|
);
|
||||||
err.span_label(
|
err.span_label(
|
||||||
span,
|
span,
|
||||||
InferCtxt::cannot_infer_msg("type", &name, &descr, parent_name, parent_descr),
|
InferCtxt::cannot_infer_msg(
|
||||||
|
span,
|
||||||
|
"type",
|
||||||
|
&name,
|
||||||
|
&descr,
|
||||||
|
use_diag,
|
||||||
|
parent_name,
|
||||||
|
parent_descr,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +514,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
// When `arg_data.name` corresponds to a type argument, show the path of the full type we're
|
// When `arg_data.name` corresponds to a type argument, show the path of the full type we're
|
||||||
// trying to infer. In the following example, `ty_msg` contains
|
// trying to infer. In the following example, `ty_msg` contains
|
||||||
// " in `std::result::Result<i32, E>`":
|
// " for `std::result::Result<i32, E>`":
|
||||||
// ```
|
// ```
|
||||||
// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
|
// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
|
||||||
// --> file.rs:L:CC
|
// --> file.rs:L:CC
|
||||||
|
@ -438,6 +532,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
error_code,
|
error_code,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let use_diag = local_visitor.found_use_diagnostic.as_ref();
|
||||||
|
if let Some(use_diag) = use_diag {
|
||||||
|
if use_diag.applies_to(err_span) {
|
||||||
|
use_diag.attach_note(&mut err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let suffix = match local_visitor.found_node_ty {
|
let suffix = match local_visitor.found_node_ty {
|
||||||
Some(ty) if ty.is_closure() => {
|
Some(ty) if ty.is_closure() => {
|
||||||
let substs =
|
let substs =
|
||||||
|
@ -460,6 +561,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
&arg_data.description,
|
&arg_data.description,
|
||||||
&arg_data.name,
|
&arg_data.name,
|
||||||
&ret,
|
&ret,
|
||||||
|
use_diag,
|
||||||
arg_data.parent_name,
|
arg_data.parent_name,
|
||||||
arg_data.parent_description,
|
arg_data.parent_description,
|
||||||
);
|
);
|
||||||
|
@ -634,9 +736,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
span,
|
span,
|
||||||
InferCtxt::cannot_infer_msg(
|
InferCtxt::cannot_infer_msg(
|
||||||
|
span,
|
||||||
kind_str,
|
kind_str,
|
||||||
&arg_data.name,
|
&arg_data.name,
|
||||||
&arg_data.description,
|
&arg_data.description,
|
||||||
|
use_diag,
|
||||||
arg_data.parent_name,
|
arg_data.parent_name,
|
||||||
arg_data.parent_description,
|
arg_data.parent_description,
|
||||||
),
|
),
|
||||||
|
@ -646,6 +750,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trait_def_from_hir_fn(&self, hir_id: hir::HirId) -> Option<DefId> {
|
||||||
|
// The DefId will be the method's trait item ID unless this is an inherent impl
|
||||||
|
if let Some((DefKind::AssocFn, def_id)) =
|
||||||
|
self.in_progress_typeck_results?.borrow().type_dependent_def(hir_id)
|
||||||
|
{
|
||||||
|
return self
|
||||||
|
.tcx
|
||||||
|
.parent(def_id)
|
||||||
|
.filter(|&parent_def_id| self.tcx.is_trait(parent_def_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// If the `FnSig` for the method call can be found and type arguments are identified as
|
/// If the `FnSig` for the method call can be found and type arguments are identified as
|
||||||
/// needed, suggest annotating the call, otherwise point out the resulting type of the call.
|
/// needed, suggest annotating the call, otherwise point out the resulting type of the call.
|
||||||
fn annotate_method_call(
|
fn annotate_method_call(
|
||||||
|
@ -711,9 +829,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
span,
|
span,
|
||||||
InferCtxt::cannot_infer_msg(
|
InferCtxt::cannot_infer_msg(
|
||||||
|
span,
|
||||||
"type",
|
"type",
|
||||||
&data.name,
|
&data.name,
|
||||||
&data.description,
|
&data.description,
|
||||||
|
None,
|
||||||
data.parent_name,
|
data.parent_name,
|
||||||
data.parent_description,
|
data.parent_description,
|
||||||
),
|
),
|
||||||
|
@ -722,16 +842,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cannot_infer_msg(
|
fn cannot_infer_msg(
|
||||||
|
span: Span,
|
||||||
kind_str: &str,
|
kind_str: &str,
|
||||||
type_name: &str,
|
type_name: &str,
|
||||||
descr: &str,
|
descr: &str,
|
||||||
|
use_diag: Option<&UseDiagnostic<'_>>,
|
||||||
parent_name: Option<String>,
|
parent_name: Option<String>,
|
||||||
parent_descr: Option<&str>,
|
parent_descr: Option<&str>,
|
||||||
) -> String {
|
) -> String {
|
||||||
|
let use_diag = use_diag.filter(|d| d.applies_to(span));
|
||||||
|
|
||||||
if type_name == "_" {
|
if type_name == "_" {
|
||||||
format!("cannot infer {}", kind_str)
|
if let Some(use_diag) = use_diag {
|
||||||
|
format!("cannot infer {} of {}", kind_str, use_diag.descr())
|
||||||
} else {
|
} else {
|
||||||
let parent_desc = if let Some(parent_name) = parent_name {
|
format!("cannot infer {}", kind_str)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let extra_descr = if let Some(parent_name) = parent_name {
|
||||||
let parent_type_descr = if let Some(parent_descr) = parent_descr {
|
let parent_type_descr = if let Some(parent_descr) = parent_descr {
|
||||||
format!(" the {}", parent_descr)
|
format!(" the {}", parent_descr)
|
||||||
} else {
|
} else {
|
||||||
|
@ -739,8 +867,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
format!(" declared on{} `{}`", parent_type_descr, parent_name)
|
format!(" declared on{} `{}`", parent_type_descr, parent_name)
|
||||||
|
} else if let Some(use_diag) = use_diag {
|
||||||
|
format!(" in {}", use_diag.type_descr())
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".into()
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: We really shouldn't be dealing with strings here
|
// FIXME: We really shouldn't be dealing with strings here
|
||||||
|
@ -749,7 +879,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
// For example: "cannot infer type for type parameter `T`"
|
// For example: "cannot infer type for type parameter `T`"
|
||||||
format!(
|
format!(
|
||||||
"cannot infer {} {} {} `{}`{}",
|
"cannot infer {} {} {} `{}`{}",
|
||||||
kind_str, preposition, descr, type_name, parent_desc
|
kind_str, preposition, descr, type_name, extra_descr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,18 +280,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
let OnUnimplementedNote { message, label, note, enclosing_scope } =
|
let OnUnimplementedNote { message, label, note, enclosing_scope } =
|
||||||
self.on_unimplemented_note(trait_ref, obligation);
|
self.on_unimplemented_note(trait_ref, obligation);
|
||||||
let have_alt_message = message.is_some() || label.is_some();
|
let have_alt_message = message.is_some() || label.is_some();
|
||||||
let is_try = self
|
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
|
||||||
.tcx
|
|
||||||
.sess
|
|
||||||
.source_map()
|
|
||||||
.span_to_snippet(span)
|
|
||||||
.map(|s| &s == "?")
|
|
||||||
.unwrap_or(false);
|
|
||||||
let is_from = self.tcx.get_diagnostic_item(sym::from_trait)
|
|
||||||
== Some(trait_ref.def_id());
|
|
||||||
let is_unsize =
|
let is_unsize =
|
||||||
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
|
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
|
||||||
let (message, note) = if is_try && is_from {
|
let (message, note) = if is_try_conversion {
|
||||||
(
|
(
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"`?` couldn't convert the error to `{}`",
|
"`?` couldn't convert the error to `{}`",
|
||||||
|
@ -319,7 +311,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
if is_try && is_from {
|
if is_try_conversion {
|
||||||
let none_error = self
|
let none_error = self
|
||||||
.tcx
|
.tcx
|
||||||
.get_diagnostic_item(sym::none_error)
|
.get_diagnostic_item(sym::none_error)
|
||||||
|
|
|
@ -13,7 +13,9 @@ error[E0282]: type annotations needed for `impl Future`
|
||||||
LL | let fut = async {
|
LL | let fut = async {
|
||||||
| --- consider giving `fut` the explicit type `impl Future`, with the type parameters specified
|
| --- consider giving `fut` the explicit type `impl Future`, with the type parameters specified
|
||||||
LL | make_unit()?;
|
LL | make_unit()?;
|
||||||
| ^ cannot infer type
|
| ^ cannot infer type of `?` error
|
||||||
|
|
|
||||||
|
= note: the `?` operation implicitly converts the error value into a type implementing `From<std::io::Error>`
|
||||||
|
|
||||||
error: aborting due to previous error; 1 warning emitted
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,9 @@ error[E0282]: type annotations needed
|
||||||
LL | let fut = async {
|
LL | let fut = async {
|
||||||
| --- consider giving `fut` a type
|
| --- consider giving `fut` a type
|
||||||
LL | make_unit()?;
|
LL | make_unit()?;
|
||||||
| ^ cannot infer type
|
| ^ cannot infer type of `?` error
|
||||||
|
|
|
||||||
|
= note: the `?` operation implicitly converts the error value into a type implementing `From<std::io::Error>`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
14
src/test/ui/inference/cannot-infer-closure-circular.rs
Normal file
14
src/test/ui/inference/cannot-infer-closure-circular.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
fn main() {
|
||||||
|
// Below we call the closure with its own return as the argument, unifying
|
||||||
|
// its inferred input and return types. We want to make sure that the generated
|
||||||
|
// error handles this gracefully, and in particular doesn't generate an extra
|
||||||
|
// note about the `?` operator in the closure body, which isn't relevant to
|
||||||
|
// the inference.
|
||||||
|
let x = |r| {
|
||||||
|
//~^ ERROR type annotations needed
|
||||||
|
let v = r?;
|
||||||
|
Ok(v)
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = x(x(Ok(())));
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
error[E0282]: type annotations needed for `std::result::Result<(), E>`
|
||||||
|
--> $DIR/cannot-infer-closure-circular.rs:7:14
|
||||||
|
|
|
||||||
|
LL | let x = |r| {
|
||||||
|
| ^ consider giving this closure parameter the explicit type `std::result::Result<(), E>`, with the type parameters specified
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0282`.
|
|
@ -2,8 +2,9 @@ error[E0282]: type annotations needed for the closure `fn((), ()) -> std::result
|
||||||
--> $DIR/cannot-infer-closure.rs:3:15
|
--> $DIR/cannot-infer-closure.rs:3:15
|
||||||
|
|
|
|
||||||
LL | Err(a)?;
|
LL | Err(a)?;
|
||||||
| ^ cannot infer type
|
| ^ cannot infer type of `?` error
|
||||||
|
|
|
|
||||||
|
= note: the `?` operation implicitly converts the error value into a type implementing `From<()>`
|
||||||
help: give this closure an explicit return type without `_` placeholders
|
help: give this closure an explicit return type without `_` placeholders
|
||||||
|
|
|
|
||||||
LL | let x = |a: (), b: ()| -> std::result::Result<(), _> {
|
LL | let x = |a: (), b: ()| -> std::result::Result<(), _> {
|
||||||
|
|
22
src/test/ui/inference/cannot-infer-partial-try-return.rs
Normal file
22
src/test/ui/inference/cannot-infer-partial-try-return.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
struct QualifiedError<E>(E);
|
||||||
|
|
||||||
|
impl<E, T> From<E> for QualifiedError<T>
|
||||||
|
where
|
||||||
|
E: std::error::Error,
|
||||||
|
T: From<E>,
|
||||||
|
{
|
||||||
|
fn from(e: E) -> QualifiedError<T> {
|
||||||
|
QualifiedError(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infallible() -> Result<(), std::convert::Infallible> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = || -> Result<_, QualifiedError<_>> {
|
||||||
|
infallible()?; //~ERROR type annotations needed
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
}
|
15
src/test/ui/inference/cannot-infer-partial-try-return.stderr
Normal file
15
src/test/ui/inference/cannot-infer-partial-try-return.stderr
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
error[E0282]: type annotations needed for the closure `fn() -> std::result::Result<(), QualifiedError<_>>`
|
||||||
|
--> $DIR/cannot-infer-partial-try-return.rs:19:9
|
||||||
|
|
|
||||||
|
LL | infallible()?;
|
||||||
|
| ^^^^^^^^^^^^^ cannot infer type of `?` error
|
||||||
|
|
|
||||||
|
= note: the `?` operation implicitly converts the error value into `QualifiedError<_>` using its implementation of `From<Infallible>`
|
||||||
|
help: give this closure an explicit return type without `_` placeholders
|
||||||
|
|
|
||||||
|
LL | let x = || -> std::result::Result<(), QualifiedError<_>> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0282`.
|
Loading…
Add table
Add a link
Reference in a new issue