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

Box trivial cyclic refs #41

Merged
merged 13 commits into from
Mar 16, 2022
Merged

Box trivial cyclic refs #41

merged 13 commits into from
Mar 16, 2022

Conversation

jmpesp
Copy link
Contributor

@jmpesp jmpesp commented Mar 14, 2022

If a struct A has a property that refers to type A, then insert a Box
for this. Note that this commit only checks for these trivial cyclic
refs.

Saw "anyOf" for Option, so match against that case

If a struct A has a property that refers to type A, then insert a Box
for this. Note that this commit only checks for these trivial cyclic
refs.

Saw "anyOf" for Option, so match against that case
@jmpesp jmpesp requested a review from ahl March 14, 2022 16:14
Copy link
Collaborator

@ahl ahl left a comment

Choose a reason for hiding this comment

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

awesome! ship it

Comment on lines 715 to 716
// Rust can emit "anyOf":[{"$ref":"#/definitions/C"},{"type":"null"} for
// Option, so match against that here.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I might say something like "short-circuit the check for mutual exclusivity below to handle the common case of an Option"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed, done in 3841159

@@ -221,6 +227,39 @@ impl TypeSpace {

// TODO Look for containment cycles that we need to break with a Box<T>

// While we aren't yet handling the general case of type containment
// cycles, it's not that bad to look at trivial cycles (A->A).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// cycles, it's not that bad to look at trivial cycles (A->A).
// cycles, it's not that bad to look at trivial cycles (A->A) (including A->Option<A>).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, I think this comment just needs a rewhack based on what's going on below now...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

rewhacked as part of 3459a0c

@@ -72,6 +72,8 @@ pub(crate) enum TypeEntryDetails {
/// reference types in particular to represent simple type aliases between
/// types named as reference targets.
Reference(TypeId),

Box(TypeId),
Copy link
Collaborator

Choose a reason for hiding this comment

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

move this up to live between Option and Array?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done in 8b70340

Comment on lines 487 to 497
TypeEntryDetails::Box(id) => {
let inner_ty = type_space
.id_to_entry
.get(id)
.expect("unresolved type id for box");

let item = inner_ty.type_ident(type_space, external);

quote! { Box<#item> }
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe move this next to Option because it's almost the same code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done in 8b70340

note, not nice code
}

// TODO unconditional
let _ = self
Copy link
Collaborator

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 explicitly ignore the return, but it certainly doesn't hurt.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has moved to the end of add_ref_types, and I removed the comment. The intention is clear from the code that we're modifying existing entries in id_to_entry unconditionally.

@jmpesp jmpesp changed the title Box struct properties with trivial cyclic refs Box trivial cyclic refs Mar 15, 2022
Copy link
Collaborator

@ahl ahl left a comment

Choose a reason for hiding this comment

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

ship it!

///
fn break_trivial_cyclic_refs(
&mut self,
type_id: &TypeId,
Copy link
Collaborator

Choose a reason for hiding this comment

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

might you want to rename this parent_type_id to make the comments below more explicit? same for parent_type_entry

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed, done in 961dbc1

Comment on lines 280 to 293
if prop.type_id == *type_id {
// A struct property directly refers to the parent type
prop.type_id = box_id
.get_or_insert_with(|| self.id_to_box(type_id))
.clone();
} else {
// A struct property optionally refers to the parent type
let mut prop_type_entry =
self.id_to_entry.get_mut(&prop.type_id).unwrap().clone();
self.break_trivial_cyclic_refs(&type_id, &mut prop_type_entry, box_id);
let _ = self
.id_to_entry
.insert(prop.type_id.clone(), prop_type_entry);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

just noting that I think this logic is repeated 3 times

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the prompt - I originally assumed I could not pull that into a method because it wouldn't make the borrow checker happy, but turns out it's possible. Done in 3d9210c

Comment on lines 268 to 274
TypeEntryDetails::Option(option_type_id) => {
if *option_type_id == *type_id {
*option_type_id = box_id
.get_or_insert_with(|| self.id_to_box(type_id))
.clone();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

are we exercising this code path with a test? I'm not sure I can imagine how we would get here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one is mainly hit when recursing, aka

struct A {
  a: Option<Box<A>>
}

}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe we want to handle the TypeDetails::Newtype case? That's the only other one I can imagine hitting trivially. It would look like struct A(Box<A>)... (and talk about a useless type!!) Certainly we'll need to consider it when if/when we deal with the general case...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done in 63481f1

.convert_schema_object(Name::Required(name), &schema.schema)
.unwrap();

// If we have converted the type already, use that, otherwise convert schema object
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// If we have converted the type already, use that, otherwise convert schema object
// In some situations, `schema_for!(T)` may actually give us two copies of the type: one in the definitions and one in the schema. This will occur in particular for cyclic types i.e. those for which the type itself is a reference.
// If we have converted the type already, use that, otherwise convert schema object

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done in bc4a146

typify-impl/src/test_util.rs Show resolved Hide resolved
@jmpesp
Copy link
Contributor Author

jmpesp commented Mar 16, 2022

There's currently a problem compiling omicron with this branch, please do not merge :)

@jmpesp
Copy link
Contributor Author

jmpesp commented Mar 16, 2022

6b34cc6 fixes it

@jmpesp jmpesp merged commit 6617df9 into oxidecomputer:main Mar 16, 2022
@jmpesp jmpesp deleted the box_cyclic_refs branch March 16, 2022 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants