Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2032,7 +2032,8 @@ impl Clone for Ty {
impl Ty {
pub fn peel_refs(&self) -> &Self {
let mut final_ty = self;
while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind {
while let TyKind::Ref(_, MutTy { ty, .. }) | TyKind::Ptr(MutTy { ty, .. }) = &final_ty.kind
{
final_ty = ty;
}
final_ty
Expand Down
39 changes: 35 additions & 4 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,11 +727,13 @@ impl<'a> Parser<'a> {
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();

// In addition to looping while we find generic bounds:
// We continue even if we find a keyword. This is necessary for error recovery on,
// for example, `impl fn()`. The only keyword that can go after generic bounds is
// `where`, so stop if it's it.
// We also continue if we find types (not traits), again for error recovery.
while self.can_begin_bound()
// Continue even if we find a keyword.
// This is necessary for error recover on, for example, `impl fn()`.
//
// The only keyword that can go after generic bounds is `where`, so stop if it's it.
|| self.token.can_begin_type()
|| (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
{
if self.token.is_keyword(kw::Dyn) {
Expand Down Expand Up @@ -938,6 +940,35 @@ impl<'a> Parser<'a> {
&& self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
&& let Some(path) = self.recover_path_from_fn()
{
path
} else if !self.token.is_path_start() && self.token.can_begin_type() && let Ok(ty) = self.parse_ty_no_plus() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue I'm seeing is that let Ok(ty) = self.parse_ty_no_plus() can fail parsing the type and will be returning an Err(Diagnostic), which gets discarded. If this happens, an ICE will occur. We need to either bubble it up (wich self.parse_ty_no_plus()?, which is fine if slightly misleading) or err.delay_as_bug() it (which isn't great without another mechanism to complain about the parse failure).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, what about matching on error and cancelling the diagnostic. I didn't properly realize that it returned a diagnostic, not just an error to be later turned into a diagnostic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With my current code the following ICEs:

fn foo<T: &match>() {}

if we ? the parse call then we get:

error: expected type, found keyword `match`
 --> <anon>:1:12
  |
1 | fn foo<T: &match>() {}
  |            ^^^^^ expected type

I'm happy with this, I don't think it's confusing. We could however add a note, something along the lines of: this error was encountered when trying to recover from a "expected a trait, found type" error before bubbling it up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do map_err(|mut err| { err.note(..); err })?, but I think even without the extra context this output would be fine.

// Instead of finding a path (a trait), we found a type.
let mut err = self.struct_span_err(ty.span, "expected a trait, found type");

// If we can recover, try to extract a path from the type. Note
// that we do not use the try operator when parsing the type because
// if it fails then we get a parser error which we don't want (we're trying
// to recover from errors, not make more).
let path = if self.may_recover()
&& matches!(ty.kind, TyKind::Ptr(..) | TyKind::Ref(..))
&& let TyKind::Path(_, path) = &ty.peel_refs().kind {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
&& let TyKind::Path(_, path) = &ty.peel_refs().kind {
&& let TyKind::Path(_, path) = &ty.peel_refs().kind
{

// Just get the indirection part of the type.
let span = ty.span.until(path.span);

err.span_suggestion_verbose(
span,
"consider removing the indirection",
"",
Applicability::MaybeIncorrect,
);

path.clone()
} else {
return Err(err);
};

err.emit();

path
} else {
self.parse_path(PathStyle::Type)?
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/generics/issue-106694.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
trait Trait {}

fn foo(_: impl &Trait) {}
//~^ ERROR expected a trait, found type

fn bar<T: &Trait>(_: T) {}
//~^ ERROR expected a trait, found type

fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
//~^ ERROR expected a trait, found type

fn foo_bad(_: impl &BadTrait) {}
//~^ ERROR expected a trait, found type
//~^^ ERROR cannot find trait `BadTrait` in this scope

fn bar_bad<T: &BadTrait>(_: T) {}
//~^ ERROR expected a trait, found type
//~^^ ERROR cannot find trait `BadTrait` in this scope

fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
//~^ ERROR expected a trait, found type
//~^^ ERROR cannot find trait `BadTrait` in this scope

fn main() {}
93 changes: 93 additions & 0 deletions tests/ui/generics/issue-106694.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
error: expected a trait, found type
--> $DIR/issue-106694.rs:3:16
|
LL | fn foo(_: impl &Trait) {}
| ^^^^^^
|
help: consider removing the indirection
|
LL - fn foo(_: impl &Trait) {}
LL + fn foo(_: impl Trait) {}
|

error: expected a trait, found type
--> $DIR/issue-106694.rs:6:11
|
LL | fn bar<T: &Trait>(_: T) {}
| ^^^^^^
|
help: consider removing the indirection
|
LL - fn bar<T: &Trait>(_: T) {}
LL + fn bar<T: Trait>(_: T) {}
|

error: expected a trait, found type
--> $DIR/issue-106694.rs:9:35
|
LL | fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
| ^^^^^^^^^^^^^^
|
help: consider removing the indirection
|
LL - fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
LL + fn partially_correct_impl(_: impl Trait + Copy) {}
|

error: expected a trait, found type
--> $DIR/issue-106694.rs:12:20
|
LL | fn foo_bad(_: impl &BadTrait) {}
| ^^^^^^^^^
|
help: consider removing the indirection
|
LL - fn foo_bad(_: impl &BadTrait) {}
LL + fn foo_bad(_: impl BadTrait) {}
|

error: expected a trait, found type
--> $DIR/issue-106694.rs:16:15
|
LL | fn bar_bad<T: &BadTrait>(_: T) {}
| ^^^^^^^^^
|
help: consider removing the indirection
|
LL - fn bar_bad<T: &BadTrait>(_: T) {}
LL + fn bar_bad<T: BadTrait>(_: T) {}
|

error: expected a trait, found type
--> $DIR/issue-106694.rs:20:39
|
LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
| ^^^^^^^^^^^^^^^^^
|
help: consider removing the indirection
|
LL - fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
LL + fn partially_correct_impl_bad(_: impl BadTrait + Copy) {}
|

error[E0405]: cannot find trait `BadTrait` in this scope
--> $DIR/issue-106694.rs:12:21
|
LL | fn foo_bad(_: impl &BadTrait) {}
| ^^^^^^^^ not found in this scope

error[E0405]: cannot find trait `BadTrait` in this scope
--> $DIR/issue-106694.rs:16:16
|
LL | fn bar_bad<T: &BadTrait>(_: T) {}
| ^^^^^^^^ not found in this scope

error[E0405]: cannot find trait `BadTrait` in this scope
--> $DIR/issue-106694.rs:20:48
|
LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
| ^^^^^^^^ not found in this scope

error: aborting due to 9 previous errors

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