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

Refactor type memory layouts and ABIs, to be more general and easier to optimize. #45225

Merged
merged 69 commits into from
Nov 20, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
fab2532
rustc_trans: move const & lvalue access helpers from adt.
eddyb Apr 25, 2017
9deea47
rustc_mir: always downcast enums, even if univariant.
eddyb Nov 18, 2017
f44b099
rustc_trans: avoid working with sizes/offsets and alignments as integ…
eddyb Jun 1, 2017
386d59d
rustc_trans: use a predictable layout for constant ADTs.
eddyb Jun 18, 2017
b8671be
rustc_trans: remove obsolete Type methods.
eddyb Jun 14, 2017
260c41b
rustc_trans: do not introspect LLVM aggregate field types.
eddyb Jun 18, 2017
5b1fdae
rustc_trans: use more of the trans::mir and ty::layout APIs throughout.
eddyb Jun 25, 2017
84b5a3d
rustc_trans: remove the in_memory_type_of distinction.
eddyb Jun 26, 2017
0a1fcc3
rustc_trans: use *[T; 0] for slice data pointers instead of *T.
eddyb Jun 26, 2017
8afa3a0
rustc_trans: always insert alignment padding, even before the first f…
eddyb Jun 25, 2017
44eef7c
rustc: do not inject discriminant fields into Layout::General's varia…
eddyb Sep 10, 2017
50a3fd0
rustc: remove useless 0 prefix from Layout::StructWrappedNullablePoin…
eddyb Sep 10, 2017
bc8e1f7
rustc: use an offset instead of a field path in Layout::StructWrapped…
eddyb Sep 10, 2017
aa811d7
rustc: remove source field path from Layout::StructWrappedNullablePoi…
eddyb Sep 11, 2017
8864668
rustc: re-complicate the TyLayout API and use better names.
eddyb Sep 12, 2017
8c4d5af
rustc: remove Ty::layout and move everything to layout_of.
eddyb Sep 12, 2017
9a0efea
rustc: pre-compute field placements out of Layout.
eddyb Sep 13, 2017
3071060
rustc_trans: treat General enums like unions.
eddyb Sep 13, 2017
1dc572b
rustc: represent the discriminant as a field for Layout::{Raw,StructW…
eddyb Sep 13, 2017
caef91d
rustc: introduce layout::Abi for reduced general ABI "passing style".
eddyb Sep 14, 2017
02276e9
rustc: collapse Layout::{Raw,StructWrapped}NullablePointer into one v…
eddyb Sep 15, 2017
335bd8e
rustc: do not track `non_zero` in Layout.
eddyb Sep 16, 2017
61c2bd9
rustc: use Primitive instead of Integer for CEnum and General discrim…
eddyb Sep 16, 2017
d318b9c
rustc: move CEnum's signedness into Primitive::Int.
eddyb Sep 16, 2017
658ebfc
rustc: give Layout::CEnum a discriminant field like Layout::General.
eddyb Sep 16, 2017
33a205b
rustc: collapse Layout::CEnum into Layout::General.
eddyb Sep 16, 2017
bd86f37
rustc: make Layout::NullablePointer a lot more like Layout::General.
eddyb Sep 16, 2017
bd51a2b
rustc: move size/alignment from Layout into layout::Abi.
eddyb Sep 17, 2017
ed788a6
rustc: store CachedLayout for each variant of enum Layout's instead o…
eddyb Sep 17, 2017
08f9f13
rustc: hide details in Layout in favor of Abi or FieldPlacement.
eddyb Sep 17, 2017
18d54aa
rustc: move layout::Struct into FieldPlacement/Abi.
eddyb Sep 19, 2017
fad9954
rustc: split layout::FieldPlacement::Linear back into Union and Array.
eddyb Sep 19, 2017
d0ab6e8
rustc_trans: compute LLVM types from type layouts, not Rust types.
eddyb Sep 19, 2017
b2d52d2
rustc: do not pub use Layout::* in layout.
eddyb Sep 19, 2017
f2e7e17
rustc_trans: pass OperandRef arguments to trans_intrinsic_call.
eddyb Sep 19, 2017
88f7032
rustc_trans: nest abi::ArgType's for fat pointers instead of eagerly …
eddyb Sep 20, 2017
1477119
rustc_trans: keep a layout instead of a type in {Lvalue,Operand}Ref.
eddyb Sep 20, 2017
3fd6b00
rustc_trans: query LLVM types from a layout instead of a Ty.
eddyb Sep 20, 2017
026214c
rustc: collapse Layout::FatPointer into Layout::Univariant.
eddyb Sep 20, 2017
b723af2
rustc_trans: go through layouts uniformly for fat pointers and variants.
eddyb Sep 21, 2017
b28f668
rustc: move size, align & primitive_align from Abi::Aggregate to layout.
eddyb Sep 22, 2017
018323f
rustc: collapse the remains of Layout into Variants (enums vs everyth…
eddyb Sep 22, 2017
de3e581
rustc: support u128 discriminant ranges.
eddyb Sep 23, 2017
abbc1dd
rustc: make TyLayout::field(NonZero<*T>, 0) return &T.
eddyb Sep 24, 2017
0190f27
rustc_trans: check for layout::I1 instead of TyBool.
eddyb Sep 24, 2017
b203a26
rustc: generalize layout::Variants::NicheFilling to niches other than 0.
eddyb Sep 24, 2017
5df25c4
rustc: remove redundant/unused fields from layout::Abi::Vector.
eddyb Sep 26, 2017
f62e43d
rustc: track validity ranges for layout::Abi::Scalar values.
eddyb Sep 26, 2017
ced5e04
rustc: optimize out uninhabited types and variants.
eddyb Sep 26, 2017
f8d5d0c
rustc_trans: compute better align/dereferenceable attributes from poi…
eddyb Oct 3, 2017
ac60872
rustc_trans: generate LLVM pointee types based on alignment.
eddyb Oct 4, 2017
f1b7cd9
rustc_trans: restrict "immediate pairs" to pairs of scalars.
eddyb Oct 5, 2017
cdeb4b0
rustc: encode scalar pairs in layout ABI.
eddyb Oct 6, 2017
c4d9ada
rustc: place ZSTs first during struct field reordering.
eddyb Oct 8, 2017
0b86972
rustc_trans: be more relaxed with non-lvalue consumes, especially ZSTs.
eddyb Oct 8, 2017
37a7521
rustc: unpack scalar newtype layout ABIs.
eddyb Oct 8, 2017
7a36141
rustc: unpack scalar pair newtype layout ABIs.
eddyb Oct 9, 2017
18ecc56
rustc_trans: support scalar pairs directly in the Rust ABI.
eddyb Oct 10, 2017
fa67abd
rustc: don't special-case Box<T> as having a pointer layout.
eddyb Oct 10, 2017
801a1a0
rustc_trans: remove type_is_fat_ptr and its uses.
eddyb Oct 10, 2017
d893285
rustc: use layout::Abi::ScalarPair for structs in more cases.
eddyb Oct 10, 2017
8437d7c
rustc: extend the niche-filling enum optimization past 2 variants.
eddyb Oct 12, 2017
753d582
rustc: rename CachedLayout to LayoutDetails.
eddyb Oct 28, 2017
95687bf
rustc_trans: (hack) use preferred alignment for atomic loads/stores.
eddyb Nov 19, 2017
fb83283
Don't glob-import overlapping variant names in test/codegen/match-opt…
eddyb Nov 18, 2017
b0812de
cargotest: temporarily use eddyb/servo to include servo/servo#19285.
eddyb Nov 19, 2017
88e4d2c
rustc_trans: work around i686-pc-windows-msvc byval align LLVM bug.
eddyb Nov 19, 2017
89e4373
rustc_trans: remove primitive_align optimization.
eddyb Nov 19, 2017
f9f5ab9
Revert "tests: Update run-make/issue-25581 to reflect how fat pointer…
eddyb Nov 19, 2017
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
Prev Previous commit
Next Next commit
rustc: generalize layout::Variants::NicheFilling to niches other than 0.
  • Loading branch information
eddyb committed Nov 19, 2017
commit b203a26efbd0d57115fc5dd40ec5410a8e5bd9da
100 changes: 61 additions & 39 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -790,17 +790,18 @@ pub enum Variants {
variants: Vec<CachedLayout>,
},

/// Two cases distinguished by a niche: the case with discriminant
/// `nndiscr` is represented by the struct `nonnull`, where field `0`
/// is known to be nonnull due to its type; if that field is null, then
/// it represents the other case, which is known to be zero sized.
/// Two cases distinguished by a niche (a value invalid for a type):
/// the variant `dataful_variant` contains a niche at an arbitrary
/// offset (field 0 of the enum), which is set to `niche_value`
/// for the other variant.
///
/// For example, `std::option::Option` instantiated at a safe pointer type
/// is represented such that `None` is a null pointer and `Some` is the
/// identity function.
/// For example, `Option<(usize, &T)>` is represented such that
/// `None` has a null pointer for the second tuple field, and
/// `Some` is the identity function (with a non-null reference).
NicheFilling {
nndiscr: u64,
discr: Primitive,
dataful_variant: usize,
niche: Primitive,
niche_value: u128,
variants: Vec<CachedLayout>,
}
}
Expand Down Expand Up @@ -1323,7 +1324,7 @@ impl<'a, 'tcx> CachedLayout {
}

for (field_index, field) in variants[i].iter().enumerate() {
if let Some((offset, discr)) = field.non_zero_field(cx)? {
if let Some((offset, niche, niche_value)) = field.find_niche(cx)? {
let mut st = vec![
univariant_uninterned(&variants[0],
&def.repr, StructKind::AlwaysSized)?,
Expand All @@ -1342,23 +1343,23 @@ impl<'a, 'tcx> CachedLayout {
..
} = st[i];

let mut discr_align = discr.align(dl);
if offset.bytes() == 0 && discr.size(dl) == size {
abi = Abi::Scalar(discr);
let mut niche_align = niche.align(dl);
if offset.bytes() == 0 && niche.size(dl) == size {
abi = Abi::Scalar(niche);
} else if let Abi::Aggregate { ref mut packed, .. } = abi {
if offset.abi_align(discr_align) != offset {
if offset.abi_align(niche_align) != offset {
*packed = true;
discr_align = dl.i8_align;
niche_align = dl.i8_align;
}
}
align = align.max(discr_align);
primitive_align = primitive_align.max(discr_align);
align = align.max(niche_align);
primitive_align = primitive_align.max(niche_align);

return Ok(tcx.intern_layout(CachedLayout {
variants: Variants::NicheFilling {
nndiscr: i as u64,

discr,
dataful_variant: i,
niche,
niche_value,
variants: st,
},
fields: FieldPlacement::Arbitrary {
Expand Down Expand Up @@ -2048,7 +2049,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {

// Discriminant field for enums (where applicable).
Variants::Tagged { discr, .. } |
Variants::NicheFilling { discr, .. } => {
Variants::NicheFilling { niche: discr, .. } => {
return cx.layout_of([discr.to_ty(tcx)][i]);
}
}
Expand Down Expand Up @@ -2084,30 +2085,48 @@ impl<'a, 'tcx> TyLayout<'tcx> {
(self.size, self.align)
}

/// Find the offset of a non-zero leaf field, starting from
/// Find the offset of a niche leaf field, starting from
/// the given type and recursing through aggregates.
/// The tuple is `(offset, primitive, source_path)`.
/// The tuple is `(offset, primitive, niche_value)`.
// FIXME(eddyb) track value ranges and traverse already optimized enums.
fn non_zero_field<C>(&self, cx: C)
-> Result<Option<(Size, Primitive)>, LayoutError<'tcx>>
fn find_niche<C>(&self, cx: C)
-> Result<Option<(Size, Primitive, u128)>, LayoutError<'tcx>>
where C: LayoutOf<Ty<'tcx>, TyLayout = Result<Self, LayoutError<'tcx>>> +
HasTyCtxt<'tcx>
{
let tcx = cx.tcx();
match (&self.variants, self.abi, &self.ty.sty) {
// FIXME(eddyb) check this via value ranges on scalars.
(_, Abi::Scalar(Int(I1, _)), _) => {
Ok(Some((Size::from_bytes(0), Int(I8, false), 2)))
}
(_, Abi::Scalar(Int(I32, _)), &ty::TyChar) => {
Ok(Some((Size::from_bytes(0), Int(I32, false), 0x10FFFF+1)))
}
(_, Abi::Scalar(Pointer), &ty::TyRef(..)) |
(_, Abi::Scalar(Pointer), &ty::TyFnPtr(..)) => {
Ok(Some((Size::from_bytes(0), Pointer)))
Ok(Some((Size::from_bytes(0), Pointer, 0)))
}
(_, Abi::Scalar(Pointer), &ty::TyAdt(def, _)) if def.is_box() => {
Ok(Some((Size::from_bytes(0), Pointer)))
Ok(Some((Size::from_bytes(0), Pointer, 0)))
}

// FIXME(eddyb) check this via value ranges on scalars.
(&Variants::Tagged { discr, .. }, _, &ty::TyAdt(def, _)) => {
if def.discriminants(tcx).all(|d| d.to_u128_unchecked() != 0) {
Ok(Some((self.fields.offset(0), discr)))
(&Variants::Tagged { discr, ref discr_range, .. }, _, _) => {
// FIXME(eddyb) support negative/wrap-around discriminant ranges.
if discr_range.start < discr_range.end {
if discr_range.start > 0 {
Ok(Some((self.fields.offset(0), discr, 0)))
} else {
let bits = discr.size(tcx).bits();
assert!(bits <= 128);
let max_value = !0u128 >> (128 - bits);
if discr_range.end < max_value {
Ok(Some((self.fields.offset(0), discr, discr_range.end + 1)))
} else {
Ok(None)
}
}
} else {
Ok(None)
}
Expand All @@ -2118,7 +2137,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
let field = self.field(cx, 0)?;
let offset = self.fields.offset(0);
if let Abi::Scalar(value) = field.abi {
Ok(Some((offset, value)))
Ok(Some((offset, value, 0)))
} else {
Ok(None)
}
Expand All @@ -2128,13 +2147,14 @@ impl<'a, 'tcx> TyLayout<'tcx> {
_ => {
if let FieldPlacement::Array { count, .. } = self.fields {
if count > 0 {
return self.field(cx, 0)?.non_zero_field(cx);
return self.field(cx, 0)?.find_niche(cx);
}
}
for i in 0..self.fields.count() {
let r = self.field(cx, i)?.non_zero_field(cx)?;
if let Some((offset, primitive)) = r {
return Ok(Some((self.fields.offset(i) + offset, primitive)));
let r = self.field(cx, i)?.find_niche(cx)?;
if let Some((offset, primitive, niche_value)) = r {
let offset = self.fields.offset(i) + offset;
return Ok(Some((offset, primitive, niche_value)));
}
}
Ok(None)
Expand Down Expand Up @@ -2165,13 +2185,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
variants.hash_stable(hcx, hasher);
}
NicheFilling {
nndiscr,
dataful_variant,
ref niche,
niche_value,
ref variants,
ref discr,
} => {
nndiscr.hash_stable(hcx, hasher);
dataful_variant.hash_stable(hcx, hasher);
niche.hash_stable(hcx, hasher);
niche_value.hash_stable(hcx, hasher);
variants.hash_stable(hcx, hasher);
discr.hash_stable(hcx, hasher);
}
}
}
Expand Down
14 changes: 5 additions & 9 deletions src/librustc_trans/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1191,17 +1191,13 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
}
}).collect()
}
layout::Variants::NicheFilling {
nndiscr,
discr,
..
} => {
let variant = self.layout.for_variant(nndiscr as usize);
layout::Variants::NicheFilling { dataful_variant, .. } => {
let variant = self.layout.for_variant(dataful_variant);
// Create a description of the non-null variant
let (variant_type_metadata, member_description_factory) =
describe_enum_variant(cx,
variant,
&adt.variants[nndiscr as usize],
&adt.variants[dataful_variant],
OptimizedDiscriminant,
self.containing_scope,
self.span);
Expand Down Expand Up @@ -1239,8 +1235,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
compute_field_path(cx, &mut name,
self.layout,
self.layout.fields.offset(0),
discr.size(cx));
name.push_str(&adt.variants[(1 - nndiscr) as usize].name.as_str());
self.layout.field(cx, 0).size);
name.push_str(&adt.variants[1 - dataful_variant].name.as_str());

// Create the (singleton) list of descriptions of union members.
vec![
Expand Down
25 changes: 16 additions & 9 deletions src/librustc_trans/mir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,22 +1122,29 @@ fn trans_const_adt<'a, 'tcx>(
},
_ => 0,
};
let discr_ty = l.field(ccx, 0).ty;
let discr = C_int(ccx.layout_of(discr_ty).llvm_type(ccx), discr as i64);
let discr_field = l.field(ccx, 0);
let discr = C_int(discr_field.llvm_type(ccx), discr as i64);
if let layout::Abi::Scalar(_) = l.abi {
Const::new(discr, t)
} else {
let discr = Const::new(discr, discr_ty);
let discr = Const::new(discr, discr_field.ty);
build_const_struct(ccx, l.for_variant(variant_index), vals, Some(discr))
}
}
layout::Variants::NicheFilling { nndiscr, .. } => {
if variant_index as u64 == nndiscr {
build_const_struct(ccx, l.for_variant(variant_index), vals, None)
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
if variant_index == dataful_variant {
build_const_struct(ccx, l.for_variant(dataful_variant), vals, None)
} else {
// Always use null even if it's not the `discrfield`th
// field; see #8506.
Const::new(C_null(ccx.layout_of(t).llvm_type(ccx)), t)
let niche = l.field(ccx, 0);
let niche_llty = niche.llvm_type(ccx);
// FIXME(eddyb) Check the actual primitive type here.
let niche_llval = if niche_value == 0 {
// HACK(eddyb) Using `C_null` as it works on all types.
C_null(niche_llty)
} else {
C_uint_big(niche_llty, niche_value)
};
build_const_struct(ccx, l, &[Const::new(niche_llval, niche.ty)], None)
}
}
}
Expand Down
59 changes: 32 additions & 27 deletions src/librustc_trans/mir/lvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc::mir::tcx::LvalueTy;
use rustc_data_structures::indexed_vec::Idx;
use base;
use builder::Builder;
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null};
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, C_uint_big};
use consts;
use type_of::LayoutLlvmExt;
use type_::Type;
Expand Down Expand Up @@ -72,10 +72,6 @@ impl Alignment {
}
}

fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool {
bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
}

#[derive(Copy, Clone, Debug)]
pub struct LvalueRef<'tcx> {
/// Pointer to the contents of the lvalue
Expand Down Expand Up @@ -325,51 +321,60 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
};
bcx.intcast(lldiscr, cast_to, signed)
}
layout::Variants::NicheFilling { nndiscr, .. } => {
let cmp = if nndiscr == 0 { llvm::IntEQ } else { llvm::IntNE };
let zero = C_null(discr.layout.llvm_type(bcx.ccx));
bcx.intcast(bcx.icmp(cmp, lldiscr, zero), cast_to, false)
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
let niche_llty = discr.layout.llvm_type(bcx.ccx);
// FIXME(eddyb) Check the actual primitive type here.
let niche_llval = if niche_value == 0 {
// HACK(eddyb) Using `C_null` as it works on all types.
C_null(niche_llty)
} else {
C_uint_big(niche_llty, niche_value)
};
let cmp = if dataful_variant == 0 { llvm::IntEQ } else { llvm::IntNE };
bcx.intcast(bcx.icmp(cmp, lldiscr, niche_llval), cast_to, false)
}
}
}

/// Set the discriminant for a new value of the given case of the given
/// representation.
pub fn trans_set_discr(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize) {
let to = self.layout.ty.ty_adt_def().unwrap()
.discriminant_for_variant(bcx.tcx(), variant_index)
.to_u128_unchecked() as u64;
match self.layout.variants {
layout::Variants::Single { index } => {
assert_eq!(to, 0);
assert_eq!(variant_index, index);
}
layout::Variants::Tagged { .. } => {
let ptr = self.project_field(bcx, 0);
let to = self.layout.ty.ty_adt_def().unwrap()
.discriminant_for_variant(bcx.tcx(), variant_index)
.to_u128_unchecked() as u64;
bcx.store(C_int(ptr.layout.llvm_type(bcx.ccx), to as i64),
ptr.llval, ptr.alignment.non_abi());
}
layout::Variants::NicheFilling { nndiscr, .. } => {
if to != nndiscr {
let use_memset = match self.layout.abi {
layout::Abi::Scalar(_) => false,
_ => target_sets_discr_via_memset(bcx)
};
if use_memset {
// Issue #34427: As workaround for LLVM bug on
// ARM, use memset of 0 on whole struct rather
// than storing null to single target field.
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
if variant_index != dataful_variant {
if bcx.sess().target.target.arch == "arm" ||
bcx.sess().target.target.arch == "aarch64" {
// Issue #34427: As workaround for LLVM bug on ARM,
// use memset of 0 before assigning niche value.
let llptr = bcx.pointercast(self.llval, Type::i8(bcx.ccx).ptr_to());
let fill_byte = C_u8(bcx.ccx, 0);
let (size, align) = self.layout.size_and_align();
let size = C_usize(bcx.ccx, size.bytes());
let align = C_u32(bcx.ccx, align.abi() as u32);
base::call_memset(bcx, llptr, fill_byte, size, align, false);
} else {
let ptr = self.project_field(bcx, 0);
bcx.store(C_null(ptr.layout.llvm_type(bcx.ccx)),
ptr.llval, ptr.alignment.non_abi());
}

let niche = self.project_field(bcx, 0);
let niche_llty = niche.layout.llvm_type(bcx.ccx);
// FIXME(eddyb) Check the actual primitive type here.
let niche_llval = if niche_value == 0 {
// HACK(eddyb) Using `C_null` as it works on all types.
C_null(niche_llty)
} else {
C_uint_big(niche_llty, niche_value)
};
bcx.store(niche_llval, niche.llval, niche.alignment.non_abi());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

// compile-flags: -Z print-type-sizes

// This file illustrates how enums with a non-null field are handled,
// modelled after cases like `Option<&u32>` and such.
// This file illustrates how niche-filling enums are handled,
// modelled after cases like `Option<&u32>`, `Option<bool>` and such.
//
// It uses NonZero directly, rather than `&_` or `Unique<_>`, because
// the test is not set up to deal with target-dependent pointer width.
Expand Down Expand Up @@ -72,4 +72,8 @@ pub fn main() {
let _x: MyOption<NonZero<u32>> = Default::default();
let _y: EmbeddedDiscr = Default::default();
let _z: MyOption<IndirectNonZero<u32>> = Default::default();
let _a: MyOption<bool> = Default::default();
let _b: MyOption<char> = Default::default();
let _c: MyOption<std::cmp::Ordering> = Default::default();
let _b: MyOption<MyOption<u8>> = Default::default();
}
Loading