Skip to content

Box trivial cyclic refs#41

Merged
jmpesp merged 13 commits into
oxidecomputer:mainfrom
jmpesp:box_cyclic_refs
Mar 16, 2022
Merged

Box trivial cyclic refs#41
jmpesp merged 13 commits into
oxidecomputer:mainfrom
jmpesp:box_cyclic_refs

Conversation

@jmpesp

@jmpesp jmpesp commented Mar 14, 2022

Copy link
Copy Markdown
Contributor

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

@ahl ahl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

awesome! ship it

Comment thread typify-impl/src/convert.rs Outdated
Comment on lines +715 to +716
// Rust can emit "anyOf":[{"$ref":"#/definitions/C"},{"type":"null"} for
// Option, so match against that here.

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

Comment thread typify-impl/src/lib.rs Outdated
// 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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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

Comment thread typify-impl/src/type_entry.rs Outdated
/// types named as reference targets.
Reference(TypeId),

Box(TypeId),

Copy link
Copy Markdown
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
Copy Markdown
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 thread typify-impl/src/type_entry.rs Outdated
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
Copy Markdown
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
Copy Markdown
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
Comment thread typify-impl/src/lib.rs Outdated
}

// TODO unconditional
let _ = self

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

@ahl ahl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ship it!

Comment thread typify-impl/src/lib.rs Outdated
///
fn break_trivial_cyclic_refs(
&mut self,
type_id: &TypeId,

Copy link
Copy Markdown
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
Copy Markdown
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 thread typify-impl/src/lib.rs Outdated
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
Copy Markdown
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
Copy Markdown
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 thread typify-impl/src/lib.rs Outdated
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
Copy Markdown
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
Copy Markdown
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>>
}

Comment thread typify-impl/src/lib.rs
}
}
}
}

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

Comment thread typify-impl/src/test_util.rs Outdated
.convert_schema_object(Name::Required(name), &schema.schema)
.unwrap();

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

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

Comment thread typify-impl/src/test_util.rs
@jmpesp

jmpesp commented Mar 16, 2022

Copy link
Copy Markdown
Contributor Author

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

@jmpesp

jmpesp commented Mar 16, 2022

Copy link
Copy Markdown
Contributor Author

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