Skip to content

Improve error messages for recursive GATs implicitly deriving Sized - #87304

Closed
jedel1043 wants to merge 4 commits into
rust-lang:masterfrom
jedel1043:issue-80626
Closed

Improve error messages for recursive GATs implicitly deriving Sized#87304
jedel1043 wants to merge 4 commits into
rust-lang:masterfrom
jedel1043:issue-80626

Conversation

@jedel1043

Copy link
Copy Markdown
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
Copy Markdown
Contributor

r? @cjgillot

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

@rust-highfiverust-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,
);

@jedel1043jedel1043Jul 20, 2021

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
Member

r? @jackh726

@jackh726jackh726 left a comment

Copy link
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
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?

def.variants
.iter()
.flat_map(|v| v.fields.last())
.flat_map(|v| &v.fields)

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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

jedel1043 commented Jul 20, 2021

Copy link
Copy Markdown
ContributorAuthor

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 deleted the issue-80626 branch September 2, 2021 21:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-reviewStatus: 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

@jedel1043@rust-highfive@jackh726@cjgillot