diff --git a/Cargo.lock b/Cargo.lock index 753853e6acde4..0c25ff3cfe476 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3747,6 +3747,7 @@ dependencies = [ "rustc_span", "rustc_symbol_mangling", "rustc_target", + "smallvec", "tempfile", "tracing", ] diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 6a6f93d50d364..a11098b11c6eb 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -16,6 +16,7 @@ libc = "0.2.50" jobserver = "0.1.22" tempfile = "3.2" pathdiff = "0.2.0" +smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 25268d9a5552f..81e905b1b5f57 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -19,8 +19,9 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD use rustc_middle::ich::NodeIdHashingMode; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt}; use rustc_target::abi::{Integer, TagEncoding, Variants}; +use smallvec::SmallVec; use std::fmt::Write; @@ -33,6 +34,8 @@ pub fn compute_debuginfo_type_name<'tcx>( t: Ty<'tcx>, qualified: bool, ) -> String { + let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name"); + let mut result = String::with_capacity(64); let mut visited = FxHashSet::default(); push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited); @@ -41,7 +44,7 @@ pub fn compute_debuginfo_type_name<'tcx>( // Pushes the name of the type as it should be stored in debuginfo on the // `output` String. See also compute_debuginfo_type_name(). -pub fn push_debuginfo_type_name<'tcx>( +fn push_debuginfo_type_name<'tcx>( tcx: TyCtxt<'tcx>, t: Ty<'tcx>, qualified: bool, @@ -84,25 +87,14 @@ pub fn push_debuginfo_type_name<'tcx>( for component_type in component_types { push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited); - output.push(','); - - // Natvis does not always like having spaces between parts of the type name - // and this causes issues when we need to write a typename in natvis, for example - // as part of a cast like the `HashMap` visualizer does. - if !cpp_like_names { - output.push(' '); - } + push_arg_separator(cpp_like_names, output); } if !component_types.is_empty() { - output.pop(); - - if !cpp_like_names { - output.pop(); - } + pop_arg_separator(output); } if cpp_like_names { - push_close_angle_bracket(tcx, output); + push_close_angle_bracket(cpp_like_names, output); } else { output.push(')'); } @@ -124,7 +116,7 @@ pub fn push_debuginfo_type_name<'tcx>( push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); if cpp_like_names { - push_close_angle_bracket(tcx, output); + push_close_angle_bracket(cpp_like_names, output); } } ty::Ref(_, inner_type, mutbl) => { @@ -150,7 +142,7 @@ pub fn push_debuginfo_type_name<'tcx>( push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); if cpp_like_names && !is_slice_or_str { - push_close_angle_bracket(tcx, output); + push_close_angle_bracket(cpp_like_names, output); } } ty::Array(inner_type, len) => { @@ -182,69 +174,97 @@ pub fn push_debuginfo_type_name<'tcx>( push_debuginfo_type_name(tcx, inner_type, true, output, visited); if cpp_like_names { - push_close_angle_bracket(tcx, output); + push_close_angle_bracket(cpp_like_names, output); } else { output.push(']'); } } ty::Dynamic(ref trait_data, ..) => { - if cpp_like_names { + let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect(); + + let has_enclosing_parens = if cpp_like_names { output.push_str("dyn$<"); + false } else { - output.push_str("dyn "); - } + if trait_data.len() > 1 && auto_traits.len() != 0 { + // We need enclosing parens because there is more than one trait + output.push_str("(dyn "); + true + } else { + output.push_str("dyn "); + false + } + }; if let Some(principal) = trait_data.principal() { let principal = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal); push_item_name(tcx, principal.def_id, qualified, output); - push_generic_params_internal(tcx, principal.substs, output, visited); - } else { - // The auto traits come ordered by `DefPathHash`, which guarantees stability if the - // environment is stable (e.g., incremental builds) but not otherwise (e.g., - // updated compiler version, different target). - // - // To avoid that causing instabilities in test output, sort the auto-traits - // alphabetically. - let mut auto_traits: Vec<_> = trait_data - .iter() - .filter_map(|predicate| { - match tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - predicate, - ) { - ty::ExistentialPredicate::AutoTrait(def_id) => { - let mut name = String::new(); - push_item_name(tcx, def_id, true, &mut name); - Some(name) - } - _ => None, - } + let principal_has_generic_params = + push_generic_params_internal(tcx, principal.substs, output, visited); + + let projection_bounds: SmallVec<[_; 4]> = trait_data + .projection_bounds() + .map(|bound| { + let ExistentialProjection { item_def_id, ty, .. } = bound.skip_binder(); + (item_def_id, ty) }) .collect(); - auto_traits.sort(); - for name in auto_traits { - output.push_str(&name); + if projection_bounds.len() != 0 { + if principal_has_generic_params { + // push_generic_params_internal() above added a `>` but we actually + // want to add more items to that list, so remove that again. + pop_close_angle_bracket(output); + } - if cpp_like_names { - output.push_str(", "); - } else { - output.push_str(" + "); + for (item_def_id, ty) in projection_bounds { + push_arg_separator(cpp_like_names, output); + + if cpp_like_names { + output.push_str("assoc$<"); + push_item_name(tcx, item_def_id, false, output); + push_arg_separator(cpp_like_names, output); + push_debuginfo_type_name(tcx, ty, true, output, visited); + push_close_angle_bracket(cpp_like_names, output); + } else { + push_item_name(tcx, item_def_id, false, output); + output.push('='); + push_debuginfo_type_name(tcx, ty, true, output, visited); + } } + + push_close_angle_bracket(cpp_like_names, output); } - // Remove the trailing joining characters. For cpp_like_names - // this is `, ` otherwise ` + `. - output.pop(); - output.pop(); - if !cpp_like_names { - output.pop(); + if auto_traits.len() != 0 { + push_auto_trait_separator(cpp_like_names, output); } } + if auto_traits.len() != 0 { + let mut auto_traits: SmallVec<[String; 4]> = auto_traits + .into_iter() + .map(|def_id| { + let mut name = String::with_capacity(20); + push_item_name(tcx, def_id, true, &mut name); + name + }) + .collect(); + auto_traits.sort_unstable(); + + for auto_trait in auto_traits { + output.push_str(&auto_trait); + push_auto_trait_separator(cpp_like_names, output); + } + + pop_auto_trait_separator(output); + } + if cpp_like_names { - push_close_angle_bracket(tcx, output); + push_close_angle_bracket(cpp_like_names, output); + } else if has_enclosing_parens { + output.push(')'); } } ty::FnDef(..) | ty::FnPtr(_) => { @@ -296,10 +316,9 @@ pub fn push_debuginfo_type_name<'tcx>( if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { push_debuginfo_type_name(tcx, parameter_type, true, output, visited); - output.push_str(", "); + push_arg_separator(cpp_like_names, output); } - output.pop(); - output.pop(); + pop_arg_separator(output); } if sig.c_variadic { @@ -405,7 +424,25 @@ pub fn push_debuginfo_type_name<'tcx>( output.push_str(&format!(", {}", variant)); } } - push_close_angle_bracket(tcx, output); + push_close_angle_bracket(true, output); + } + + const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + "; + + fn push_auto_trait_separator(cpp_like_names: bool, output: &mut String) { + if cpp_like_names { + push_arg_separator(cpp_like_names, output); + } else { + output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR); + } + } + + fn pop_auto_trait_separator(output: &mut String) { + if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) { + output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len()); + } else { + pop_arg_separator(output); + } } } @@ -466,13 +503,15 @@ fn push_generic_params_internal<'tcx>( substs: SubstsRef<'tcx>, output: &mut String, visited: &mut FxHashSet>, -) { +) -> bool { if substs.non_erasable_generics().next().is_none() { - return; + return false; } debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs)); + let cpp_like_names = cpp_like_names(tcx); + output.push('<'); for type_parameter in substs.non_erasable_generics() { @@ -486,13 +525,12 @@ fn push_generic_params_internal<'tcx>( other => bug!("Unexpected non-erasable generic: {:?}", other), } - output.push_str(", "); + push_arg_separator(cpp_like_names, output); } + pop_arg_separator(output); + push_close_angle_bracket(cpp_like_names, output); - output.pop(); - output.pop(); - - push_close_angle_bracket(tcx, output); + true } fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: &mut String) { @@ -541,20 +579,50 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: } pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) { + let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name"); let mut visited = FxHashSet::default(); push_generic_params_internal(tcx, substs, output, &mut visited); } -fn push_close_angle_bracket<'tcx>(tcx: TyCtxt<'tcx>, output: &mut String) { +fn push_close_angle_bracket(cpp_like_names: bool, output: &mut String) { // MSVC debugger always treats `>>` as a shift, even when parsing templates, // so add a space to avoid confusion. - if cpp_like_names(tcx) && output.ends_with('>') { + if cpp_like_names && output.ends_with('>') { output.push(' ') }; output.push('>'); } +fn pop_close_angle_bracket(output: &mut String) { + assert!(output.ends_with('>'), "'output' does not end with '>': {}", output); + output.pop(); + if output.ends_with(' ') { + output.pop(); + } +} + +fn push_arg_separator(cpp_like_names: bool, output: &mut String) { + // Natvis does not always like having spaces between parts of the type name + // and this causes issues when we need to write a typename in natvis, for example + // as part of a cast like the `HashMap` visualizer does. + if cpp_like_names { + output.push(','); + } else { + output.push_str(", "); + }; +} + +fn pop_arg_separator(output: &mut String) { + if output.ends_with(' ') { + output.pop(); + } + + assert!(output.ends_with(',')); + + output.pop(); +} + fn cpp_like_names(tcx: TyCtxt<'_>) -> bool { tcx.sess.target.is_like_msvc } diff --git a/src/test/debuginfo/function-names.rs b/src/test/debuginfo/function-names.rs index 20a49f0bd26eb..28ab176ba509a 100644 --- a/src/test/debuginfo/function-names.rs +++ b/src/test/debuginfo/function-names.rs @@ -52,21 +52,21 @@ // cdb-command:x a!function_names::*::impl_function* // cdb-check:[...] a!function_names::Mod1::TestStruct2::impl_function (void) // cdb-check:[...] a!function_names::TestStruct1::impl_function (void) -// cdb-check:[...] a!function_names::GenericStruct::impl_function (void) +// cdb-check:[...] a!function_names::GenericStruct::impl_function (void) // Trait implementations // cdb-command:x a!function_names::*::trait_function* // cdb-check:[...] a!function_names::impl$3::trait_function (void) +// cdb-check:[...] a!function_names::impl$6::trait_function (void) // cdb-check:[...] a!function_names::impl$1::trait_function (void) -// cdb-check:[...] a!function_names::impl$6::trait_function (void) // cdb-check:[...] a!function_names::impl$5::trait_function3 (void) // cdb-check:[...] a!function_names::Mod1::impl$1::trait_function (void) // Closure // cdb-command:x a!function_names::*::closure* +// cdb-check:[...] a!function_names::impl$2::impl_function::closure$0 (void) // cdb-check:[...] a!function_names::main::closure$0 (void) // cdb-check:[...] a!function_names::generic_func::closure$0 (void) -// cdb-check:[...] a!function_names::impl$2::impl_function::closure$0 (void) // Generator // cdb-command:x a!function_names::*::generator* diff --git a/src/test/debuginfo/generic-struct.rs b/src/test/debuginfo/generic-struct.rs index c0135de1219d3..5fa5ce8009935 100644 --- a/src/test/debuginfo/generic-struct.rs +++ b/src/test/debuginfo/generic-struct.rs @@ -44,21 +44,21 @@ // cdb-command:g // cdb-command:dx int_int -// cdb-check:int_int [Type: generic_struct::AGenericStruct] +// cdb-check:int_int [Type: generic_struct::AGenericStruct] // cdb-check:[...]key : 0 [Type: int] // cdb-check:[...]value : 1 [Type: int] // cdb-command:dx int_float -// cdb-check:int_float [Type: generic_struct::AGenericStruct] +// cdb-check:int_float [Type: generic_struct::AGenericStruct] // cdb-check:[...]key : 2 [Type: int] // cdb-check:[...]value : 3.500000 [Type: double] // cdb-command:dx float_int -// cdb-check:float_int [Type: generic_struct::AGenericStruct] +// cdb-check:float_int [Type: generic_struct::AGenericStruct] // cdb-check:[...]key : 4.500000 [Type: double] // cdb-check:[...]value : 5 [Type: int] // cdb-command:dx float_int_float -// cdb-check:float_int_float [Type: generic_struct::AGenericStruct >] +// cdb-check:float_int_float [Type: generic_struct::AGenericStruct >] // cdb-check:[...]key : 6.500000 [Type: double] -// cdb-check:[...]value [Type: generic_struct::AGenericStruct] +// cdb-check:[...]value [Type: generic_struct::AGenericStruct] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/msvc-pretty-enums.rs b/src/test/debuginfo/msvc-pretty-enums.rs index f248fcd839122..9463f82c79749 100644 --- a/src/test/debuginfo/msvc-pretty-enums.rs +++ b/src/test/debuginfo/msvc-pretty-enums.rs @@ -87,8 +87,8 @@ // cdb-check: [+0x000] discriminant : 0x[...] [Type: enum$, 1, [...], Some>::Discriminant$] // cdb-command: dx -r2 l,! -// cdb-check:l,! : $T2 [Type: enum$ >, Ok>] -// cdb-check: [+0x000] Ok [Type: enum$ >, Ok>::Ok] +// cdb-check:l,! : $T2 [Type: enum$ >, Ok>] +// cdb-check: [+0x000] Ok [Type: enum$ >, Ok>::Ok] // cdb-check: [+0x000] __0 : 0x2a [Type: unsigned int] pub enum CStyleEnum { diff --git a/src/test/debuginfo/pretty-std-collections-hash.rs b/src/test/debuginfo/pretty-std-collections-hash.rs index ede1557871257..40bde8606996a 100644 --- a/src/test/debuginfo/pretty-std-collections-hash.rs +++ b/src/test/debuginfo/pretty-std-collections-hash.rs @@ -10,7 +10,7 @@ // cdb-command: g // cdb-command: dx hash_set,d -// cdb-check:hash_set,d [...] : { len=15 } [Type: [...]::HashSet] +// cdb-check:hash_set,d [...] : { len=15 } [Type: [...]::HashSet] // cdb-check: [len] : 15 [Type: [...]] // cdb-check: [capacity] : [...] // cdb-check: [[...]] [...] : 0 [Type: u64] @@ -44,7 +44,7 @@ // cdb-check: [[...]] [...] : 14 [Type: u64] // cdb-command: dx hash_map,d -// cdb-check:hash_map,d [...] : { len=15 } [Type: [...]::HashMap] +// cdb-check:hash_map,d [...] : { len=15 } [Type: [...]::HashMap] // cdb-check: [len] : 15 [Type: [...]] // cdb-check: [capacity] : [...] // cdb-check: ["0x0"] : 0 [Type: unsigned __int64] diff --git a/src/test/debuginfo/pretty-std.rs b/src/test/debuginfo/pretty-std.rs index d5a6e148b7a66..a190a29eec2a5 100644 --- a/src/test/debuginfo/pretty-std.rs +++ b/src/test/debuginfo/pretty-std.rs @@ -79,7 +79,7 @@ // cdb-check: [3] : 3 [Type: int] // cdb-command: dx vec,d -// cdb-check:vec,d [...] : { len=4 } [Type: [...]::Vec] +// cdb-check:vec,d [...] : { len=4 } [Type: [...]::Vec] // cdb-check: [len] : 4 [Type: [...]] // cdb-check: [capacity] : [...] [Type: [...]] // cdb-check: [0] : 4 [Type: unsigned __int64] diff --git a/src/test/debuginfo/result-types.rs b/src/test/debuginfo/result-types.rs index a075c437c463b..c0d905a6acc4e 100644 --- a/src/test/debuginfo/result-types.rs +++ b/src/test/debuginfo/result-types.rs @@ -7,11 +7,11 @@ // cdb-command: g // cdb-command: dx x,d -// cdb-check:x,d : Ok [Type: enum$ >] +// cdb-check:x,d : Ok [Type: enum$ >] // cdb-check: [...] __0 : -3 [Type: int] // cdb-command: dx y -// cdb-check:y : Err [Type: enum$ >] +// cdb-check:y : Err [Type: enum$ >] // cdb-check: [...] __0 : "Some error message" [Type: str] fn main() diff --git a/src/test/debuginfo/type-names.rs b/src/test/debuginfo/type-names.rs index d1f322fa76cca..3497f0afb2cb0 100644 --- a/src/test/debuginfo/type-names.rs +++ b/src/test/debuginfo/type-names.rs @@ -117,7 +117,11 @@ // gdb-check:type = &mut dyn type_names::Trait2> // gdb-command:whatis no_principal_trait -// gdb-check:type = alloc::boxed::Box +// gdb-check:type = alloc::boxed::Box<(dyn core::marker::Send + core::marker::Sync), alloc::alloc::Global> + +// gdb-command:whatis has_associated_type_trait +// gdb-check:type = &(dyn type_names::Trait3 + core::marker::Send) + // BARE FUNCTIONS // gdb-command:whatis rust_fn @@ -169,7 +173,7 @@ // 0-sized structs appear to be optimized away in some cases, so only check the structs that do // actually appear. // cdb-command:dv /t *_struct -// cdb-check:struct type_names::GenericStruct, f64> mut_generic_struct = [...] +// cdb-check:struct type_names::GenericStruct,f64> mut_generic_struct = [...] // ENUMS // cdb-command:dv /t *_enum_* @@ -186,15 +190,15 @@ // BOX // cdb-command:dv /t box* -// cdb-check:struct tuple$,i32> box1 = [...] -// cdb-check:struct tuple$ >, alloc::alloc::Global>,i32> box2 = [...] +// cdb-check:struct tuple$,i32> box1 = [...] +// cdb-check:struct tuple$ >,alloc::alloc::Global>,i32> box2 = [...] // REFERENCES // cdb-command:dv /t *ref* // cdb-check:struct tuple$,i32> ref1 = [...] -// cdb-check:struct tuple$ >,i32> ref2 = [...] +// cdb-check:struct tuple$ >,i32> ref2 = [...] // cdb-check:struct tuple$,i32> mut_ref1 = [...] -// cdb-check:struct tuple$, f64> >,i32> mut_ref2 = [...] +// cdb-check:struct tuple$,f64> >,i32> mut_ref2 = [...] // RAW POINTERS // cdb-command:dv /t *_ptr* @@ -209,31 +213,31 @@ // cdb-command:dv /t *vec* // cdb-check:struct tuple$,i16> fixed_size_vec1 = [...] // cdb-check:struct tuple$,i16> fixed_size_vec2 = [...] -// cdb-check:struct alloc::vec::Vec vec1 = [...] -// cdb-check:struct alloc::vec::Vec, alloc::alloc::Global> vec2 = [...] +// cdb-check:struct alloc::vec::Vec vec1 = [...] +// cdb-check:struct alloc::vec::Vec,alloc::alloc::Global> vec2 = [...] // cdb-command:dv /t slice* // cdb-check:struct slice$ slice1 = [...] // cdb-check:struct slice$ > slice2 = [...] // TRAITS // cdb-command:dv /t *_trait -// cdb-check:struct ref_mut$ > > > generic_mut_ref_trait = [...] -// cdb-check:struct ref$ > > generic_ref_trait = [...] -// cdb-check:struct alloc::boxed::Box >, alloc::alloc::Global> generic_box_trait = [...] -// cdb-check:struct alloc::boxed::Box, alloc::alloc::Global> box_trait = [...] +// cdb-check:struct ref_mut$ > > > generic_mut_ref_trait = [...] +// cdb-check:struct ref$ > > generic_ref_trait = [...] +// cdb-check:struct alloc::boxed::Box >,alloc::alloc::Global> generic_box_trait = [...] +// cdb-check:struct alloc::boxed::Box,alloc::alloc::Global> box_trait = [...] // cdb-check:struct ref$ > ref_trait = [...] // cdb-check:struct ref_mut$ > mut_ref_trait = [...] -// cdb-check:struct alloc::boxed::Box, alloc::alloc::Global> no_principal_trait = [...] -// cdb-check:struct ref$ > has_associated_type_trait = struct ref$ > +// cdb-check:struct alloc::boxed::Box,alloc::alloc::Global> no_principal_trait = [...] +// cdb-check:struct ref$ >,core::marker::Send> > has_associated_type_trait = struct ref$ >,core::marker::Send> > // BARE FUNCTIONS // cdb-command:dv /t *_fn* -// cdb-check:struct tuple$),usize> unsafe_fn_with_return_value = [...] +// cdb-check:struct tuple$),usize> unsafe_fn_with_return_value = [...] // cdb-check:struct tuple$ extern_c_fn_with_return_value = [...] // cdb-check:struct tuple$ rust_fn_with_return_value = [...] -// cdb-check:struct tuple$ >),usize> unsafe_fn = [...] +// cdb-check:struct tuple$ >),usize> unsafe_fn = [...] // cdb-check:struct tuple$ extern_c_fn = [...] -// cdb-check:struct tuple$ >, enum$ >, 1, [...], Some>),usize> rust_fn = [...] +// cdb-check:struct tuple$ >,enum$ >, 1, [...], Some>),usize> rust_fn = [...] // cdb-command:dv /t *_function* // cdb-check:struct tuple$, ...),usize> variadic_function = [...] // cdb-check:struct tuple$ generic_function_struct3 = [...] @@ -306,14 +310,14 @@ trait Trait1 { trait Trait2 { fn dummy(&self, _: T1, _: T2) {} } -trait Trait3 { +trait Trait3 { type AssocType; - fn dummy(&self) {} + fn dummy(&self) -> T { panic!() } } impl Trait1 for isize {} impl Trait2 for isize {} -impl Trait3 for isize { +impl Trait3 for isize { type AssocType = isize; } @@ -404,8 +408,8 @@ fn main() { let ref_trait = &0_isize as &dyn Trait1; let mut mut_int1 = 0_isize; let mut_ref_trait = (&mut mut_int1) as &mut dyn Trait1; - let no_principal_trait = (box 0_isize) as Box; - let has_associated_type_trait = &0_isize as &dyn Trait3; + let no_principal_trait = (box 0_isize) as Box<(dyn Send + Sync)>; + let has_associated_type_trait = &0_isize as &(dyn Trait3 + Send); let generic_box_trait = (box 0_isize) as Box>; let generic_ref_trait = (&0_isize) as &dyn Trait2;