Skip to content
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

Improve error messages for recursive GATs implicitly deriving Sized #87304

Closed
wants to merge 4 commits into from

Conversation

jedel1043
Copy link
Contributor

Fixes #80626

  • Implemented a new error message recommending the user to add a ?Sized constraint in case a recursive GAT is found.
  • Fixed a bug where a type would not be constrained to be Sized if it wasn't on the last position of an ADT.
  • Added tests for both fixes.

@rust-highfive
Copy link
Collaborator

r? @cjgillot

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 20, 2021
Comment on lines +303 to +312
let (span, separator) = match param.bounds {
[] => (param.span.shrink_to_hi(), ":"),
[.., bound] => (bound.span().shrink_to_hi(), " +"),
};
err.span_suggestion_verbose(
span,
"consider relaxing the implicit `Sized` restriction",
format!("{} ?Sized", separator),
Applicability::MachineApplicable,
);
Copy link
Contributor Author

@jedel1043 jedel1043 Jul 20, 2021

Choose a reason for hiding this comment

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

When we have something like Type<T: Sized> this suggestion will recommend to do Type<T: Sized + ?Sized>, which is just incorrect. Maybe we could instead suggest to replace Sized with ?Sized when there's a Sized inside param.bounds.
Nonetheless, this works for the trivial case.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, there should be a flag about implicit Sized somewhere and we should probably honor that.

Comment on lines 269 to 287
let parameter_size = assoc_generics.params.len();
if parameter_size == 0 {
return false;
}

assert!(substs.len() >= parameter_size);

let index = match substs
.get(substs.len() - parameter_size..)
.map(|gens| {
gens.iter()
.enumerate()
.find_map(|(i, param)| if param == &tail_ty.into() { Some(i) } else { None })
})
.flatten()
{
Some(index) => index,
None => return false,
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a better way to do this? I did what I could, but I'm still not 100% sure this would work on every case.

@jackh726
Copy link
Member

r? @jackh726

@rust-highfive rust-highfive assigned jackh726 and unassigned cjgillot Jul 20, 2021
Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

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

Ran out of time, but some comments...

Overall, I guess this is just to give the "you can add a ?Sized" suggestion? I'm on the fence on if that's actually helpful. Niko and I discussed this but yesterday; the proper fix is to make Sized coinductive (#83647), but that's a bit more than just GATs.

type Allocated<T> = Box<T>;
}

fn main() {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to test runtime behavior here; this can just be empty with a // check-pass

Comment on lines +8 to +12
help: consider relaxing the implicit `Sized` restriction
|
LL | type Allocated<T: ?Sized>;
| ^^^^^^^^

Copy link
Member

Choose a reason for hiding this comment

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

On one hand...yes, this is a suggestion that should work. On the other, is this a suggestion that is normally helpful?

.flat_map(|v| v.fields.last())
.flat_map(|v| &v.fields)
Copy link
Member

Choose a reason for hiding this comment

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

So, this isn't needed (i.e. this isn't a bug). We know that all fields except the last for all variants must be Sized for the struct to be well formed. (This check is done elsewhere.)


let parent_kind = parent_ty.kind();
let child_kind = child_ty.kind();
debug!(
Copy link
Member

Choose a reason for hiding this comment

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

FYI you can do debug!(?parent_kind, ?child_kind). But, I feel like there are a lot of redundant debug statements here...(for example, we should get all the same info from the debug call above)

_ => return false,
};

let hir_id = self.tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
Copy link
Member

Choose a reason for hiding this comment

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

So, this would fail if the parent is defined in another crate?

Comment on lines +264 to +295
let (assoc_generics, _assoc_span) = match node {
Node::TraitItem(hir::TraitItem {
generics,
kind: hir::TraitItemKind::Type(..),
span,
..
}) => (generics, span),
_ => return false,
};

let parameter_size = assoc_generics.params.len();
if parameter_size == 0 {
return false;
}

assert!(substs.len() >= parameter_size);

let index = match substs
.get(substs.len() - parameter_size..)
.map(|gens| {
gens.iter()
.enumerate()
.find_map(|(i, param)| if param == &child_ty.into() { Some(i) } else { None })
})
.flatten()
{
Some(index) => index,
None => return false,
};
debug!("report_overflow_error_cycle: index = {:?}", index);

let param = &assoc_generics.params[index];
Copy link
Member

Choose a reason for hiding this comment

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

This all feels a bit weird to me. But running out of time right now to think more about this.

Comment on lines +303 to +312
let (span, separator) = match param.bounds {
[] => (param.span.shrink_to_hi(), ":"),
[.., bound] => (bound.span().shrink_to_hi(), " +"),
};
err.span_suggestion_verbose(
span,
"consider relaxing the implicit `Sized` restriction",
format!("{} ?Sized", separator),
Applicability::MachineApplicable,
);
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, there should be a flag about implicit Sized somewhere and we should probably honor that.

@jedel1043
Copy link
Contributor Author

jedel1043 commented Jul 20, 2021

Overall, I guess this is just to give the "you can add a ?Sized" suggestion? I'm on the fence on if that's actually helpful. Niko and I discussed this but yesterday; the proper fix is to make Sized coinductive (#83647), but that's a bit more than just GATs.

So the proper fix would be to make Sized coinductive, and then adjust ty_is_representable to detect when a trait implementor's associated type (Or the default value of an associated type) causes a recursive loop?

@jedel1043 jedel1043 closed this Jul 21, 2021
@jedel1043 jedel1043 deleted the issue-80626 branch September 2, 2021 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enum variant with GAT field fails to derive Sized
4 participants