Skip to content

Make const/fn return params more suggestable #106887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2945,12 +2945,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
if r.is_erased() { tcx.lifetimes.re_static } else { r }
});
let span = ast_ty.span;
tcx.sess.emit_err(TypeofReservedKeywordUsed {
span,
ty,
opt_sugg: Some((span, Applicability::MachineApplicable))
.filter(|_| ty.is_suggestable(tcx, false)),
});
let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) {
(ty, Some((span, Applicability::MachineApplicable)))
} else {
(ty, None)
};
tcx.sess.emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg });

ty
}
Expand Down
32 changes: 12 additions & 20 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1199,28 +1199,22 @@ fn infer_return_ty_for_fn_sig<'tcx>(
visitor.visit_ty(ty);
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
let ret_ty = fn_sig.output();
if ret_ty.is_suggestable(tcx, false) {
if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) {
diag.span_suggestion(
ty.span,
"replace with the correct return type",
ret_ty,
Applicability::MachineApplicable,
);
} else if matches!(ret_ty.kind(), ty::FnDef(..)) {
let fn_sig = ret_ty.fn_sig(tcx);
if fn_sig
.skip_binder()
.inputs_and_output
.iter()
.all(|t| t.is_suggestable(tcx, false))
{
diag.span_suggestion(
ty.span,
"replace with the correct return type",
fn_sig,
Applicability::MachineApplicable,
);
}
} else if matches!(ret_ty.kind(), ty::FnDef(..))
&& let Some(fn_sig) = ret_ty.fn_sig(tcx).make_suggestable(tcx, false)
{
diag.span_suggestion(
ty.span,
"replace with the correct return type",
fn_sig,
Applicability::MachineApplicable,
);
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
diag.span_suggestion(
ty.span,
Expand Down Expand Up @@ -1280,9 +1274,7 @@ fn suggest_impl_trait<'tcx>(
let trait_name = tcx.item_name(trait_def_id);
let args_tuple = substs.type_at(1);
let ty::Tuple(types) = *args_tuple.kind() else { return None; };
if !types.is_suggestable(tcx, false) {
return None;
}
let types = types.make_suggestable(tcx, false)?;
let maybe_ret =
if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
Some(format!(
Expand Down Expand Up @@ -1337,7 +1329,7 @@ fn suggest_impl_trait<'tcx>(
// FIXME(compiler-errors): We may benefit from resolving regions here.
if ocx.select_where_possible().is_empty()
&& let item_ty = infcx.resolve_vars_if_possible(item_ty)
&& item_ty.is_suggestable(tcx, false)
&& let Some(item_ty) = item_ty.make_suggestable(tcx, false)
&& let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty)
{
return Some(sugg);
Expand Down
47 changes: 14 additions & 33 deletions compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
use rustc_middle::ty::{
self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable,
};
use rustc_span::symbol::Ident;
use rustc_span::{Span, DUMMY_SP};

Expand Down Expand Up @@ -845,37 +847,23 @@ fn infer_placeholder_type<'a>(
) -> Ty<'a> {
// Attempts to make the type nameable by turning FnDefs into FnPtrs.
struct MakeNameable<'tcx> {
success: bool,
tcx: TyCtxt<'tcx>,
}

impl<'tcx> MakeNameable<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Self {
MakeNameable { success: true, tcx }
}
}

impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !self.success {
return ty;
}

match ty.kind() {
let ty = match *ty.kind() {
ty::FnDef(def_id, substs) => {
self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id).subst(self.tcx, substs))
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
}
// FIXME: non-capturing closures should also suggest a function pointer
ty::Closure(..) | ty::Generator(..) => {
self.success = false;
ty
}
_ => ty.super_fold_with(self),
}
_ => ty,
};

ty.super_fold_with(self)
}
}

Expand All @@ -898,15 +886,11 @@ fn infer_placeholder_type<'a>(
suggestions.clear();
}

// Suggesting unnameable types won't help.
let mut mk_nameable = MakeNameable::new(tcx);
let ty = mk_nameable.fold_ty(ty);
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
if let Some(sugg_ty) = sugg_ty {
if let Some(ty) = ty.make_suggestable(tcx, false) {
err.span_suggestion(
span,
&format!("provide a type for the {item}", item = kind),
format!("{colon} {sugg_ty}"),
format!("{colon} {ty}"),
Applicability::MachineApplicable,
);
} else {
Expand All @@ -923,15 +907,12 @@ fn infer_placeholder_type<'a>(
let mut diag = bad_placeholder(tcx, vec![span], kind);

if !ty.references_error() {
let mut mk_nameable = MakeNameable::new(tcx);
let ty = mk_nameable.fold_ty(ty);
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
if let Some(sugg_ty) = sugg_ty {
if let Some(ty) = ty.make_suggestable(tcx, false) {
diag.span_suggestion(
span,
"replace with the correct type",
sugg_ty,
Applicability::MaybeIncorrect,
ty,
Applicability::MachineApplicable,
);
} else {
with_forced_trimmed_paths!(diag.span_note(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return true;
}
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
if found.is_suggestable(self.tcx, false) {
if let Some(found) = found.make_suggestable(self.tcx, false) {
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
return true;
} else if let ty::Closure(_, substs) = found.kind()
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,9 +490,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(output_def_id) = output_def_id
&& let Some(trait_def_id) = trait_def_id
&& self.tcx.parent(output_def_id) == trait_def_id
&& output_ty.is_suggestable(self.tcx, false)
&& let Some(output_ty) = output_ty.make_suggestable(self.tcx, false)
{
Some(("Output", *output_ty))
Some(("Output", output_ty))
} else {
None
}
Expand Down
95 changes: 91 additions & 4 deletions compiler/rustc_middle/src/ty/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
use std::ops::ControlFlow;

use crate::ty::{
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst,
InferTy, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
TypeSuperVisitable, TypeVisitor,
};

use rustc_data_structures::fx::FxHashMap;
Expand Down Expand Up @@ -76,7 +77,7 @@ impl<'tcx> Ty<'tcx> {
}
}

pub trait IsSuggestable<'tcx> {
pub trait IsSuggestable<'tcx>: Sized {
/// Whether this makes sense to suggest in a diagnostic.
///
/// We filter out certain types and constants since they don't provide
Expand All @@ -87,15 +88,21 @@ pub trait IsSuggestable<'tcx> {
/// Only if `infer_suggestable` is true, we consider type and const
/// inference variables to be suggestable.
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;

fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<Self>;
}

impl<'tcx, T> IsSuggestable<'tcx> for T
where
T: TypeVisitable<'tcx>,
T: TypeVisitable<'tcx> + TypeFoldable<'tcx>,
{
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
}

fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<T> {
self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok()
}
}

pub fn suggest_arbitrary_trait_bound<'tcx>(
Expand Down Expand Up @@ -509,3 +516,83 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
c.super_visit_with(self)
}
}

pub struct MakeSuggestableFolder<'tcx> {
tcx: TyCtxt<'tcx>,
infer_suggestable: bool,
}

impl<'tcx> FallibleTypeFolder<'tcx> for MakeSuggestableFolder<'tcx> {
type Error = ();

fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
let t = match *t.kind() {
Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,

FnDef(def_id, substs) => {
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
}

// FIXME(compiler-errors): We could replace these with infer, I guess.
Closure(..)
| Infer(..)
| Generator(..)
| GeneratorWitness(..)
| Bound(_, _)
| Placeholder(_)
| Error(_) => {
return Err(());
}

Alias(Opaque, AliasTy { def_id, .. }) => {
let parent = self.tcx.parent(def_id);
if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
&& let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
&& parent_opaque_def_id == def_id
{
t
} else {
return Err(());
}
}

Param(param) => {
// FIXME: It would be nice to make this not use string manipulation,
// but it's pretty hard to do this, since `ty::ParamTy` is missing
// sufficient info to determine if it is synthetic, and we don't
// always have a convenient way of getting `ty::Generics` at the call
// sites we invoke `IsSuggestable::is_suggestable`.
if param.name.as_str().starts_with("impl ") {
return Err(());
}

t
}

_ => t,
};

t.try_super_fold_with(self)
}

fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ()> {
let c = match c.kind() {
ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => c,

ConstKind::Infer(..)
| ConstKind::Bound(..)
| ConstKind::Placeholder(..)
| ConstKind::Error(..) => {
return Err(());
}

_ => c,
};

c.try_super_fold_with(self)
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> {
/// the infallible methods of this trait to ensure that the two APIs
/// are coherent.
pub trait TypeFolder<'tcx>: FallibleTypeFolder<'tcx, Error = !> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
fn tcx(&self) -> TyCtxt<'tcx>;

fn fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T>
where
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// run-rustfix

#![allow(unused)]

struct Wrapper<T>(T);

fn bar() -> Wrapper<fn()> { Wrapper(foo) }
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types

fn foo() {}

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// run-rustfix

#![allow(unused)]

struct Wrapper<T>(T);

fn bar() -> _ { Wrapper(foo) }
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types

fn foo() {}

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/suggestions/suggest-fn-ptr-for-fn-item-in-fn-ret.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-fn-ptr-for-fn-item-in-fn-ret.rs:7:13
|
LL | fn bar() -> _ { Wrapper(foo) }
| ^
| |
| not allowed in type signatures
| help: replace with the correct return type: `Wrapper<fn()>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0121`.