From 6e7ec0c5b4b961a9260e72613b7d5477f09d7187 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 12 Nov 2021 14:45:19 +0100 Subject: [PATCH 1/9] Simplify collect_in_band_defs. --- compiler/rustc_ast_lowering/src/lib.rs | 71 +++++++++++++------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d0fbc2d0f11d4..ec5022679d439 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -646,31 +646,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// parameter while `f` is running (and restored afterwards). fn collect_in_band_defs( &mut self, - parent_def_id: LocalDefId, - anonymous_lifetime_mode: AnonymousLifetimeMode, - f: impl FnOnce(&mut Self) -> (Vec>, T), - ) -> (Vec>, T) { + f: impl FnOnce(&mut Self) -> T, + ) -> (Vec<(Span, ParamName)>, T) { assert!(!self.is_collecting_in_band_lifetimes); assert!(self.lifetimes_to_define.is_empty()); - let old_anonymous_lifetime_mode = self.anonymous_lifetime_mode; - - self.anonymous_lifetime_mode = anonymous_lifetime_mode; self.is_collecting_in_band_lifetimes = true; - let (in_band_ty_params, res) = f(self); + let res = f(self); self.is_collecting_in_band_lifetimes = false; - self.anonymous_lifetime_mode = old_anonymous_lifetime_mode; - - let lifetimes_to_define = self.lifetimes_to_define.split_off(0); - let params = lifetimes_to_define - .into_iter() - .map(|(span, hir_name)| self.lifetime_to_generic_param(span, hir_name, parent_def_id)) - .chain(in_band_ty_params.into_iter()) - .collect(); - - (params, res) + let lifetimes_to_define = std::mem::take(&mut self.lifetimes_to_define); + (lifetimes_to_define, res) } /// Converts a lifetime into a new generic parameter. @@ -785,27 +772,39 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { anonymous_lifetime_mode: AnonymousLifetimeMode, f: impl FnOnce(&mut Self, &mut Vec>) -> T, ) -> (hir::Generics<'hir>, T) { - let (in_band_defs, (mut lowered_generics, res)) = - self.with_in_scope_lifetime_defs(&generics.params, |this| { - this.collect_in_band_defs(parent_def_id, anonymous_lifetime_mode, |this| { - let mut params = Vec::new(); - // Note: it is necessary to lower generics *before* calling `f`. - // When lowering `async fn`, there's a final step when lowering - // the return type that assumes that all in-scope lifetimes have - // already been added to either `in_scope_lifetimes` or - // `lifetimes_to_define`. If we swapped the order of these two, - // in-band-lifetimes introduced by generics or where-clauses - // wouldn't have been added yet. - let generics = this.lower_generics_mut( - generics, - ImplTraitContext::Universal(&mut params, this.current_hir_id_owner), - ); - let res = f(this, &mut params); - (params, (generics, res)) + let (lifetimes_to_define, (mut lowered_generics, impl_trait_defs, res)) = self + .collect_in_band_defs(|this| { + this.with_anonymous_lifetime_mode(anonymous_lifetime_mode, |this| { + this.with_in_scope_lifetime_defs(&generics.params, |this| { + let mut impl_trait_defs = Vec::new(); + // Note: it is necessary to lower generics *before* calling `f`. + // When lowering `async fn`, there's a final step when lowering + // the return type that assumes that all in-scope lifetimes have + // already been added to either `in_scope_lifetimes` or + // `lifetimes_to_define`. If we swapped the order of these two, + // in-band-lifetimes introduced by generics or where-clauses + // wouldn't have been added yet. + let generics = this.lower_generics_mut( + generics, + ImplTraitContext::Universal( + &mut impl_trait_defs, + this.current_hir_id_owner, + ), + ); + let res = f(this, &mut impl_trait_defs); + (generics, impl_trait_defs, res) + }) }) }); - lowered_generics.params.extend(in_band_defs); + lowered_generics.params.extend( + lifetimes_to_define + .into_iter() + .map(|(span, hir_name)| { + self.lifetime_to_generic_param(span, hir_name, parent_def_id) + }) + .chain(impl_trait_defs), + ); let lowered_generics = lowered_generics.into_generics(self.arena); (lowered_generics, res) From 72b6f7049c0903ac266dc67510b655441528bdf2 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 29 Nov 2021 22:59:13 +0100 Subject: [PATCH 2/9] Use collect_in_band_defs for async lifetime captures. --- compiler/rustc_ast_lowering/src/lib.rs | 98 +++++++++---------- .../ui/async-await/generics-and-bounds.rs | 11 +++ 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index ec5022679d439..667e94656f600 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -648,15 +648,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, f: impl FnOnce(&mut Self) -> T, ) -> (Vec<(Span, ParamName)>, T) { - assert!(!self.is_collecting_in_band_lifetimes); - assert!(self.lifetimes_to_define.is_empty()); - self.is_collecting_in_band_lifetimes = true; + let was_collecting = std::mem::replace(&mut self.is_collecting_in_band_lifetimes, true); + let len = self.lifetimes_to_define.len(); let res = f(self); - self.is_collecting_in_band_lifetimes = false; - - let lifetimes_to_define = std::mem::take(&mut self.lifetimes_to_define); + let lifetimes_to_define = self.lifetimes_to_define.split_off(len); + self.is_collecting_in_band_lifetimes = was_collecting; (lifetimes_to_define, res) } @@ -1688,18 +1686,29 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // this is because the elided lifetimes from the return type // should be figured out using the ordinary elision rules, and // this desugaring achieves that. + + debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", self.in_scope_lifetimes); + debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", self.lifetimes_to_define); + + // Calculate all the lifetimes that should be captured + // by the opaque type. This should include all in-scope + // lifetime parameters, including those defined in-band. // - // The variable `input_lifetimes_count` tracks the number of - // lifetime parameters to the opaque type *not counting* those - // lifetimes elided in the return type. This includes those - // that are explicitly declared (`in_scope_lifetimes`) and - // those elided lifetimes we found in the arguments (current - // content of `lifetimes_to_define`). Next, we will process - // the return type, which will cause `lifetimes_to_define` to - // grow. - let input_lifetimes_count = self.in_scope_lifetimes.len() + self.lifetimes_to_define.len(); - - let mut lifetime_params = Vec::new(); + // `lifetime_params` is a vector of tuple (span, parameter name, lifetime name). + + // Input lifetime like `'a` or `'1`: + let mut lifetime_params: Vec<_> = self + .in_scope_lifetimes + .iter() + .cloned() + .map(|name| (name.ident().span, name, hir::LifetimeName::Param(name))) + .chain( + self.lifetimes_to_define + .iter() + .map(|&(span, name)| (span, name, hir::LifetimeName::Param(name))), + ) + .collect(); + self.with_hir_id_owner(opaque_ty_node_id, |this| { // We have to be careful to get elision right here. The // idea is that we create a lifetime parameter for each @@ -1709,34 +1718,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and // hence the elision takes place at the fn site. - let future_bound = this - .with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| { - this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span) + let (lifetimes_to_define, future_bound) = + this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| { + this.collect_in_band_defs(|this| { + this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span) + }) }); - debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound); + debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define); - // Calculate all the lifetimes that should be captured - // by the opaque type. This should include all in-scope - // lifetime parameters, including those defined in-band. - // - // Note: this must be done after lowering the output type, - // as the output type may introduce new in-band lifetimes. - lifetime_params = this - .in_scope_lifetimes - .iter() - .cloned() - .map(|name| (name.ident().span, name)) - .chain(this.lifetimes_to_define.iter().cloned()) - .collect(); - - debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", this.in_scope_lifetimes); - debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", this.lifetimes_to_define); + lifetime_params.extend( + // Output lifetime like `'_`: + lifetimes_to_define + .into_iter() + .map(|(span, name)| (span, name, hir::LifetimeName::Implicit(false))), + ); debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params); let generic_params = - this.arena.alloc_from_iter(lifetime_params.iter().map(|(span, hir_name)| { - this.lifetime_to_generic_param(*span, *hir_name, opaque_ty_def_id) + this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name, _)| { + this.lifetime_to_generic_param(span, hir_name, opaque_ty_def_id) })); let opaque_ty_item = hir::OpaqueTy { @@ -1770,25 +1771,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // For the "output" lifetime parameters, we just want to // generate `'_`. - let mut generic_args = Vec::with_capacity(lifetime_params.len()); - generic_args.extend(lifetime_params[..input_lifetimes_count].iter().map( - |&(span, hir_name)| { - // Input lifetime like `'a` or `'1`: + let generic_args = + self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _, name)| { GenericArg::Lifetime(hir::Lifetime { hir_id: self.next_id(), span: self.lower_span(span), - name: hir::LifetimeName::Param(hir_name), + name, }) - }, - )); - generic_args.extend(lifetime_params[input_lifetimes_count..].iter().map(|&(span, _)| - // Output lifetime like `'_`. - GenericArg::Lifetime(hir::Lifetime { - hir_id: self.next_id(), - span: self.lower_span(span), - name: hir::LifetimeName::Implicit(false), - }))); - let generic_args = self.arena.alloc_from_iter(generic_args); + })); // Create the `Foo<...>` reference itself. Note that the `type // Foo = impl Trait` is, internally, created as a child of the diff --git a/src/test/ui/async-await/generics-and-bounds.rs b/src/test/ui/async-await/generics-and-bounds.rs index 963b19b34a620..90ab0c01f5450 100644 --- a/src/test/ui/async-await/generics-and-bounds.rs +++ b/src/test/ui/async-await/generics-and-bounds.rs @@ -2,6 +2,8 @@ // edition:2018 // compile-flags: --crate-type lib +#![feature(in_band_lifetimes)] + use std::future::Future; pub async fn simple_generic() {} @@ -71,6 +73,10 @@ pub fn call_with_ref_block<'a>(f: &'a (impl Foo + 'a)) -> impl Future impl Future + 'a { + async move { f.foo() } +} + pub fn async_block_with_same_generic_params_unifies() { let mut a = call_generic_bound_block(FooType); a = call_generic_bound_block(FooType); @@ -85,4 +91,9 @@ pub fn async_block_with_same_generic_params_unifies() { let f_two = FooType; let mut d = call_with_ref_block(&f_one); d = call_with_ref_block(&f_two); + + let f_one = FooType; + let f_two = FooType; + let mut d = call_with_ref_block_in_band(&f_one); + d = call_with_ref_block_in_band(&f_two); } From 8576ab45e4fc192524ab615f3309d9b552528828 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 30 Nov 2021 19:11:35 +0100 Subject: [PATCH 3/9] Store impl_trait_fn inside OpaqueTyOrigin. --- compiler/rustc_ast_lowering/src/item.rs | 9 +-- compiler/rustc_ast_lowering/src/lib.rs | 25 +++--- .../src/region_infer/opaque_types.rs | 2 +- compiler/rustc_hir/src/hir.rs | 9 ++- .../error_reporting/nice_region_error/util.rs | 2 +- .../rustc_infer/src/infer/opaque_types.rs | 22 +++--- compiler/rustc_middle/src/ty/mod.rs | 18 +++-- compiler/rustc_resolve/src/late/lifetimes.rs | 15 +++- compiler/rustc_ty_utils/src/ty.rs | 4 +- compiler/rustc_typeck/src/astconv/mod.rs | 13 ++- compiler/rustc_typeck/src/check/check.rs | 8 +- compiler/rustc_typeck/src/collect.rs | 79 +++++++++++-------- compiler/rustc_typeck/src/collect/type_of.rs | 8 +- 13 files changed, 118 insertions(+), 96 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 692f39368243a..ca7a64e254e14 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -247,12 +247,7 @@ impl<'hir> LoweringContext<'_, 'hir> { AnonymousLifetimeMode::PassThrough, |this, idty| { let ret_id = asyncness.opt_return_id(); - this.lower_fn_decl( - &decl, - Some((fn_def_id.to_def_id(), idty)), - true, - ret_id, - ) + this.lower_fn_decl(&decl, Some((fn_def_id, idty)), true, ret_id) }, ); let sig = hir::FnSig { @@ -1264,7 +1259,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |this, idty| { this.lower_fn_decl( &sig.decl, - Some((fn_def_id.to_def_id(), idty)), + Some((fn_def_id, idty)), impl_trait_return_allow, is_async, ) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 667e94656f600..c04b0471cb72f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -228,7 +228,7 @@ enum ImplTraitContext<'b, 'a> { ReturnPositionOpaqueTy { /// `DefId` for the parent function, used to look up necessary /// information later. - fn_def_id: DefId, + fn_def_id: LocalDefId, /// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn, origin: hir::OpaqueTyOrigin, }, @@ -1377,7 +1377,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_opaque_impl_trait( &mut self, span: Span, - fn_def_id: Option, + fn_def_id: Option, origin: hir::OpaqueTyOrigin, opaque_ty_node_id: NodeId, capturable_lifetimes: Option<&FxHashSet>, @@ -1449,7 +1449,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: lctx.lower_span(span), }, bounds: hir_bounds, - impl_trait_fn: fn_def_id, origin, }; @@ -1519,7 +1518,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_fn_decl( &mut self, decl: &FnDecl, - mut in_band_ty_params: Option<(DefId, &mut Vec>)>, + mut in_band_ty_params: Option<(LocalDefId, &mut Vec>)>, impl_trait_return_allow: bool, make_ret_async: Option, ) -> &'hir hir::FnDecl<'hir> { @@ -1577,7 +1576,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Some((def_id, _)) if impl_trait_return_allow => { ImplTraitContext::ReturnPositionOpaqueTy { fn_def_id: def_id, - origin: hir::OpaqueTyOrigin::FnReturn, + origin: hir::OpaqueTyOrigin::FnReturn(def_id), } } _ => ImplTraitContext::disallowed(), @@ -1632,7 +1631,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_async_fn_ret_ty( &mut self, output: &FnRetTy, - fn_def_id: DefId, + fn_def_id: LocalDefId, opaque_ty_node_id: NodeId, ) -> hir::FnRetTy<'hir> { debug!( @@ -1747,8 +1746,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: this.lower_span(span), }, bounds: arena_vec![this; future_bound], - impl_trait_fn: Some(fn_def_id), - origin: hir::OpaqueTyOrigin::AsyncFn, + origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id), }; trace!("exist ty from async fn def id: {:#?}", opaque_ty_def_id); @@ -1794,7 +1792,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_async_fn_output_type_to_future_bound( &mut self, output: &FnRetTy, - fn_def_id: DefId, + fn_def_id: LocalDefId, span: Span, ) -> hir::GenericBound<'hir> { // Compute the `T` in `Future` from the return type. @@ -1805,7 +1803,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // generates. let context = ImplTraitContext::ReturnPositionOpaqueTy { fn_def_id, - origin: hir::OpaqueTyOrigin::FnReturn, + origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), }; self.lower_ty(ty, context) } @@ -2442,17 +2440,12 @@ impl<'hir> GenericArgsCtor<'hir> { } } +#[tracing::instrument(level = "debug")] fn lifetimes_from_impl_trait_bounds( opaque_ty_id: NodeId, bounds: hir::GenericBounds<'_>, lifetimes_to_include: Option<&FxHashSet>, ) -> Vec<(hir::LifetimeName, Span)> { - debug!( - "lifetimes_from_impl_trait_bounds(opaque_ty_id={:?}, \ - bounds={:#?})", - opaque_ty_id, bounds, - ); - // This visitor walks over `impl Trait` bounds and creates defs for all lifetimes that // appear in the bounds, excluding lifetimes that are created within the bounds. // E.g., `'a`, `'b`, but not `'c` in `impl for<'c> SomeTrait<'a, 'b, 'c>`. diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 4eb7be542e7a1..76b3be7976c61 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -173,7 +173,7 @@ fn check_opaque_type_parameter_valid( // fn foo() -> foo::<'static..'static>::Foo<'l0..'lm>. // // which would error here on all of the `'static` args. - OpaqueTyOrigin::FnReturn | OpaqueTyOrigin::AsyncFn => return true, + OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true, // Check these OpaqueTyOrigin::TyAlias => {} } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 487ae87052ba3..d393ea6893849 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2248,7 +2248,6 @@ pub struct BareFnTy<'hir> { pub struct OpaqueTy<'hir> { pub generics: Generics<'hir>, pub bounds: GenericBounds<'hir>, - pub impl_trait_fn: Option, pub origin: OpaqueTyOrigin, } @@ -2256,9 +2255,9 @@ pub struct OpaqueTy<'hir> { #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { /// `-> impl Trait` - FnReturn, + FnReturn(LocalDefId), /// `async fn` - AsyncFn, + AsyncFn(LocalDefId), /// type aliases: `type Foo = impl Trait;` TyAlias, } @@ -2809,7 +2808,9 @@ impl ItemKind<'_> { Some(match *self { ItemKind::Fn(_, ref generics, _) | ItemKind::TyAlias(_, ref generics) - | ItemKind::OpaqueTy(OpaqueTy { ref generics, impl_trait_fn: None, .. }) + | ItemKind::OpaqueTy(OpaqueTy { + ref generics, origin: OpaqueTyOrigin::TyAlias, .. + }) | ItemKind::Enum(_, ref generics) | ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 90bc5b3b2fed1..04eceecc5f072 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -107,7 +107,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, - origin: hir::OpaqueTyOrigin::AsyncFn, + origin: hir::OpaqueTyOrigin::AsyncFn(..), .. }), .. diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index d9ab4ae1eda29..754d5a6ed7251 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -276,7 +276,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!(?concrete_ty); let first_own_region = match opaque_defn.origin { - hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => { + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => { // We lower // // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> @@ -463,20 +463,24 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let parent_def_id = self.infcx.defining_use_anchor; let (in_definition_scope, origin) = match tcx.hir().expect_item(def_id).kind { + // Async `impl Trait` + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn(parent), + .. + }) => (parent == parent_def_id, hir::OpaqueTyOrigin::AsyncFn(parent)), // Anonymous `impl Trait` hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: Some(parent), - origin, + origin: hir::OpaqueTyOrigin::FnReturn(parent), .. - }) => (parent == parent_def_id.to_def_id(), origin), + }) => (parent == parent_def_id, hir::OpaqueTyOrigin::FnReturn(parent)), // Named `type Foo = impl Bar;` hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: None, - origin, + origin: hir::OpaqueTyOrigin::TyAlias, .. - }) => { - (may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), origin) - } + }) => ( + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), + hir::OpaqueTyOrigin::TyAlias, + ), ref itemkind => { span_bug!( self.value_span, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7e1804673df2c..fd1409949f0a7 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2055,13 +2055,17 @@ impl<'tcx> TyCtxt<'tcx> { } } -/// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition. -pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - if let Some(def_id) = def_id.as_local() { - if let Node::Item(item) = tcx.hir().get(tcx.hir().local_def_id_to_hir_id(def_id)) { - if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { - return opaque_ty.impl_trait_fn; - } +/// Yields the parent function's `LocalDefId` if `def_id` is an `impl Trait` definition. +pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + let def_id = def_id.as_local()?; + if let Node::Item(item) = tcx.hir().get(tcx.hir().local_def_id_to_hir_id(def_id)) { + if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { + return match opaque_ty.origin { + hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => { + Some(parent) + } + hir::OpaqueTyOrigin::TyAlias => None, + }; } } None diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 1ff33689b53c7..c94c56df75b78 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -968,7 +968,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let (generics, bounds) = match opaque_ty.kind { // Named opaque `impl Trait` types are reached via `TyKind::Path`. // This arm is for `impl Trait` in the types of statics, constants and locals. - hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: None, .. }) => { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias, + .. + }) => { intravisit::walk_ty(self, ty); // Elided lifetimes are not allowed in non-return @@ -985,7 +988,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } // RPIT (return position impl trait) hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: Some(_), + origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..), ref generics, bounds, .. @@ -1695,7 +1698,11 @@ fn compute_object_lifetime_defaults( hir::ItemKind::Struct(_, ref generics) | hir::ItemKind::Union(_, ref generics) | hir::ItemKind::Enum(_, ref generics) - | hir::ItemKind::OpaqueTy(hir::OpaqueTy { ref generics, impl_trait_fn: None, .. }) + | hir::ItemKind::OpaqueTy(hir::OpaqueTy { + ref generics, + origin: hir::OpaqueTyOrigin::TyAlias, + .. + }) | hir::ItemKind::TyAlias(_, ref generics) | hir::ItemKind::Trait(_, _, ref generics, ..) => { let result = object_lifetime_defaults_for_item(tcx, generics); @@ -2067,7 +2074,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { .. }) = self.tcx.hir().get(parent_hir_id) { - if opaque.origin != hir::OpaqueTyOrigin::AsyncFn { + if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) { continue 'lifetimes; } // We want to do this only if the liftime identifier is already defined diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 711a6f2fbebdd..74a015d4c348e 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -248,7 +248,7 @@ fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // The param_env of an impl Trait type is its defining function's param_env if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { - return param_env(tcx, parent); + return param_env(tcx, parent.to_def_id()); } // Compute the bounds on Self and the type parameters. @@ -313,7 +313,7 @@ fn well_formed_types_in_env<'tcx>( // The environment of an impl Trait type is its defining function's environment. if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { - return well_formed_types_in_env(tcx, parent); + return well_formed_types_in_env(tcx, parent.to_def_id()); } // Compute the bounds on `Self` and the type parameters. diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 08261fedd4a25..908adeb22b364 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -2336,9 +2336,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let def_id = item_id.def_id.to_def_id(); match opaque_ty.kind { - hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { - self.impl_trait_ty_to_ty(def_id, lifetimes, impl_trait_fn.is_some()) - } + hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => self + .impl_trait_ty_to_ty( + def_id, + lifetimes, + matches!( + origin, + hir::OpaqueTyOrigin::FnReturn(..) + | hir::OpaqueTyOrigin::AsyncFn(..) + ), + ), ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), } } diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 2061f955d968f..a6b16e7f0d4d8 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -541,7 +541,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( } if let ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn, + origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..), .. }) = item.kind { @@ -567,7 +567,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( visitor.visit_item(&item); let is_async = match item.kind { ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { - matches!(origin, hir::OpaqueTyOrigin::AsyncFn) + matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..)) } _ => unreachable!(), }; @@ -604,7 +604,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>( ) -> Result<(), ErrorReported> { if tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs).is_err() { match origin { - hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span), + hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span), _ => opaque_type_cycle_error(tcx, def_id, span), } Err(ErrorReported) @@ -635,7 +635,7 @@ fn check_opaque_meets_bounds<'tcx>( ) { match origin { // Checked when type checking the function containing them. - hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => return, + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => return, // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias => {} } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 67a3053c60786..e8d28dd75d6aa 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -594,7 +594,11 @@ fn type_param_predicates( ItemKind::Fn(.., ref generics, _) | ItemKind::Impl(hir::Impl { ref generics, .. }) | ItemKind::TyAlias(_, ref generics) - | ItemKind::OpaqueTy(OpaqueTy { ref generics, impl_trait_fn: None, .. }) + | ItemKind::OpaqueTy(OpaqueTy { + ref generics, + origin: hir::OpaqueTyOrigin::TyAlias, + .. + }) | ItemKind::Enum(_, ref generics) | ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) => generics, @@ -793,7 +797,10 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } // Desugared from `impl Trait`, so visited by the function's return type. - hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(_), .. }) => {} + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..), + .. + }) => {} // Don't call `type_of` on opaque types, since that depends on type // checking function bodies. `check_item_type` ensures that it's called @@ -1488,15 +1495,18 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { Some(tcx.typeck_root_def_id(def_id)) } Node::Item(item) => match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { - impl_trait_fn.or_else(|| { - let parent_id = tcx.hir().get_parent_item(hir_id); - assert!(parent_id != hir_id && parent_id != CRATE_HIR_ID); - debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); - // Opaque types are always nested within another item, and - // inherit the generics of the item. - Some(tcx.hir().local_def_id(parent_id).to_def_id()) - }) + ItemKind::OpaqueTy(hir::OpaqueTy { + origin: + hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + .. + }) => Some(fn_def_id.to_def_id()), + ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { + let parent_id = tcx.hir().get_parent_item(hir_id); + assert!(parent_id != hir_id && parent_id != CRATE_HIR_ID); + debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); + // Opaque types are always nested within another item, and + // inherit the generics of the item. + Some(tcx.hir().local_def_id(parent_id).to_def_id()) } _ => None, }, @@ -2051,31 +2061,32 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP generics } ItemKind::OpaqueTy(OpaqueTy { - bounds: _, - impl_trait_fn, + origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..), + .. + }) => { + // return-position impl trait + // + // We don't inherit predicates from the parent here: + // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}` + // then the return type is `f::<'static, T>::{{opaque}}`. + // + // If we inherited the predicates of `f` then we would + // require that `T: 'static` to show that the return + // type is well-formed. + // + // The only way to have something with this opaque type + // is from the return type of the containing function, + // which will ensure that the function's predicates + // hold. + return ty::GenericPredicates { parent: None, predicates: &[] }; + } + ItemKind::OpaqueTy(OpaqueTy { ref generics, - origin: _, + origin: hir::OpaqueTyOrigin::TyAlias, + .. }) => { - if impl_trait_fn.is_some() { - // return-position impl trait - // - // We don't inherit predicates from the parent here: - // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}` - // then the return type is `f::<'static, T>::{{opaque}}`. - // - // If we inherited the predicates of `f` then we would - // require that `T: 'static` to show that the return - // type is well-formed. - // - // The only way to have something with this opaque type - // is from the return type of the containing function, - // which will ensure that the function's predicates - // hold. - return ty::GenericPredicates { parent: None, predicates: &[] }; - } else { - // type-alias impl trait - generics - } + // type-alias impl trait + generics } _ => NO_GENERICS, diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index b684844744de3..af199ca99460f 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -394,13 +394,13 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); tcx.mk_adt(def, substs) } - ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }) => { + ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { find_opaque_ty_constraints(tcx, def_id) } // Opaque types desugared from `impl Trait`. - ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: Some(owner), .. }) => { + ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => { let concrete_ty = tcx - .mir_borrowck(owner.expect_local()) + .mir_borrowck(owner) .concrete_opaque_types .get_value_matching(|(key, _)| key.def_id == def_id.to_def_id()) .copied() @@ -413,7 +413,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { ), ); if let Some(ErrorReported) = - tcx.typeck(owner.expect_local()).tainted_by_errors + tcx.typeck(owner).tainted_by_errors { // Some error in the // owner fn prevented us from populating From 54ff72132c860726f40e0ff6d547b039de0a1323 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 7 Dec 2021 22:49:07 +0100 Subject: [PATCH 4/9] Simplify match. --- .../rustc_infer/src/infer/opaque_types.rs | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 754d5a6ed7251..c2ef0b41e27bf 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -461,39 +461,29 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { if let Some(def_id) = def_id.as_local() { let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let parent_def_id = self.infcx.defining_use_anchor; - let (in_definition_scope, origin) = match tcx.hir().expect_item(def_id).kind - { + let item_kind = &tcx.hir().expect_item(def_id).kind; + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { + span_bug!( + self.value_span, + "weird opaque type: {:#?}, {:#?}", + ty.kind(), + item_kind + ) + }; + let in_definition_scope = match *origin { // Async `impl Trait` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn(parent), - .. - }) => (parent == parent_def_id, hir::OpaqueTyOrigin::AsyncFn(parent)), + hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, // Anonymous `impl Trait` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(parent), - .. - }) => (parent == parent_def_id, hir::OpaqueTyOrigin::FnReturn(parent)), + hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, // Named `type Foo = impl Bar;` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias, - .. - }) => ( - may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), - hir::OpaqueTyOrigin::TyAlias, - ), - ref itemkind => { - span_bug!( - self.value_span, - "weird opaque type: {:#?}, {:#?}", - ty.kind(), - itemkind - ) + hir::OpaqueTyOrigin::TyAlias => { + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) } }; if in_definition_scope { let opaque_type_key = OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; - return self.fold_opaque_ty(ty, opaque_type_key, origin); + return self.fold_opaque_ty(ty, opaque_type_key, *origin); } debug!( From 14f4ffae3224a7846c6bab33f9ca8593ec7416cf Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Wed, 8 Dec 2021 17:55:50 -0500 Subject: [PATCH 5/9] Use spare_capacity_mut instead of invalid unchecked indexing --- library/alloc/src/str.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 104f5556566f4..69495f31c32ca 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -178,12 +178,20 @@ where unsafe { let pos = result.len(); - let target = result.get_unchecked_mut(pos..reserved_len); + let target = result.spare_capacity_mut().get_unchecked_mut(..reserved_len - pos); + + // Convert the separator and slices to slices of MaybeUninit + // to simplify implementation in specialize_for_lengths + let sep_uninit = core::slice::from_raw_parts(sep.as_ptr().cast(), sep.len()); + let iter_uninit = iter.map(|it| { + let it = it.borrow().as_ref(); + core::slice::from_raw_parts(it.as_ptr().cast(), it.len()) + }); // copy separator and slices over without bounds checks // generate loops with hardcoded offsets for small separators // massive improvements possible (~ x2) - let remain = specialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); + let remain = specialize_for_lengths!(sep_uninit, target, iter_uninit; 0, 1, 2, 3, 4); // A weird borrow implementation may return different // slices for the length calculation and the actual copy. From e36da67a5ea12cdc0dd589f89b57b21d70385720 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Wed, 8 Dec 2021 16:05:31 -0800 Subject: [PATCH 6/9] rustdoc: Show type layout for type aliases At first, you might think, "Why not just click through to the aliased type?" But, if a type alias instantiates all of the generic parameters of the aliased type, then it can show layout info even though the aliased type cannot (because we can't compute layout for generic types). So, I think it's useful to show layout info for type aliases. This is a followup of 78d4b453ad2e19d44011b26fc55c949bff5dba3d (originally part of #83501). --- src/librustdoc/html/render/print_item.rs | 1 + src/test/rustdoc/type-layout.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index e139ac8581e72..2fae5495b0af1 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -976,6 +976,7 @@ fn item_typedef( // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. render_assoc_items(w, cx, it, def_id, AssocItemRender::All); + document_type_layout(w, cx, def_id); } fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { diff --git a/src/test/rustdoc/type-layout.rs b/src/test/rustdoc/type-layout.rs index 0868486fa59cd..4eea9809ac58f 100644 --- a/src/test/rustdoc/type-layout.rs +++ b/src/test/rustdoc/type-layout.rs @@ -50,6 +50,18 @@ pub struct GenericLifetimes<'a>(&'a str); // @has - '(unsized)' pub struct Unsized([u8]); +// @has type_layout/type.TypeAlias.html 'Size: ' +// @has - ' bytes' +pub type TypeAlias = X; + +// @has type_layout/type.GenericTypeAlias.html 'Size: ' +// @has - '8 bytes' +pub type GenericTypeAlias = (Generic<(u32, ())>, Generic); + +// Regression test for the rustdoc equivalent of #85103. +// @has type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.' +pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>; + // @!has type_layout/trait.MyTrait.html 'Size: ' pub trait MyTrait {} From 769a70726963647c2be6b5a470547bf74088bcbe Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 7 Dec 2021 13:29:20 +1100 Subject: [PATCH 7/9] Improve the readability of `List`. This commit does the following. - Expands on some of the things already mentioned in comments. - Describes the uniqueness assumption, which is critical but wasn't mentioned at all. - Rewrites `empty()` into a clearer form, as provided by Daniel Henry-Mantilla on Zulip. - Reorders things slightly so that more important things are higher up, and incidental things are lower down, which makes reading the code easier. --- compiler/rustc_middle/src/ty/list.rs | 140 +++++++++++++++++---------- 1 file changed, 89 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 1dceda6c7aad0..adba7d131592e 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -1,7 +1,5 @@ use crate::arena::Arena; - use rustc_serialize::{Encodable, Encoder}; - use std::alloc::Layout; use std::cmp::Ordering; use std::fmt; @@ -12,49 +10,69 @@ use std::ops::Deref; use std::ptr; use std::slice; -extern "C" { - /// A dummy type used to force `List` to be unsized while not requiring references to it be wide - /// pointers. - type OpaqueListContents; -} - -/// A wrapper for slices with the additional invariant -/// that the slice is interned and no other slice with -/// the same contents can exist in the same context. -/// This means we can use pointer for both -/// equality comparisons and hashing. -/// -/// Unlike slices, the types contained in `List` are expected to be `Copy` -/// and iterating over a `List` returns `T` instead of a reference. -/// -/// Note: `Slice` was already taken by the `Ty`. +/// `List` is a bit like `&[T]`, but with some critical differences. +/// - IMPORTANT: Every `List` is *required* to have unique contents. The +/// type's correctness relies on this, *but it does not enforce it*. +/// Therefore, any code that creates a `List` must ensure uniqueness +/// itself. In practice this is achieved by interning. +/// - The length is stored within the `List`, so `&List` is a thin +/// pointer. +/// - Because of this, you cannot get a `List` that is a sub-list of another +/// `List`. You can get a sub-slice `&[T]`, however. +/// - `List` can be used with `CopyTaggedPtr`, which is useful within +/// structs whose size must be minimized. +/// - Because of the uniqueness assumption, we can use the address of a +/// `List` for faster equality comparisons and hashing. +/// - `T` must be `Copy`. This lets `List` be stored in a dropless arena and +/// iterators return a `T` rather than a `&T`. +/// - `T` must not be zero-sized. #[repr(C)] pub struct List { len: usize, + + /// Although this claims to be a zero-length array, in practice `len` + /// elements are actually present. data: [T; 0], + opaque: OpaqueListContents, } -unsafe impl<'a, T: 'a> rustc_data_structures::tagged_ptr::Pointer for &'a List { - const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; - #[inline] - fn into_usize(self) -> usize { - self as *const List as usize - } - #[inline] - unsafe fn from_usize(ptr: usize) -> Self { - &*(ptr as *const List) - } - unsafe fn with_ref R>(ptr: usize, f: F) -> R { - // Self: Copy so this is fine - let ptr = Self::from_usize(ptr); - f(&ptr) - } +extern "C" { + /// A dummy type used to force `List` to be unsized while not requiring + /// references to it be wide pointers. + type OpaqueListContents; } -unsafe impl Sync for List {} +impl List { + /// Returns a reference to the (unique, static) empty list. + #[inline(always)] + pub fn empty<'a>() -> &'a List { + #[repr(align(64))] + struct MaxAlign; + + assert!(mem::align_of::() <= mem::align_of::()); + + #[repr(C)] + struct InOrder(T, U); + + // The empty slice is static and contains a single `0` usize (for the + // length) that is 64-byte aligned, thus featuring the necessary + // trailing padding for elements with up to 64-byte alignment. + static EMPTY_SLICE: InOrder = InOrder(0, MaxAlign); + unsafe { &*(&EMPTY_SLICE as *const _ as *const List) } + } +} impl List { + /// Allocates a list from `arena` and copies the contents of `slice` into it. + /// + /// WARNING: the contents *must be unique*, such that no list with these + /// contents has been previously created. If not, operations such as `eq` + /// and `hash` might give incorrect results. + /// + /// Panics if `T` is `Drop`, or `T` is zero-sized, or the slice is empty + /// (because the empty list exists statically, and is available via + /// `empty()`). #[inline] pub(super) fn from_arena<'tcx>(arena: &'tcx Arena<'tcx>, slice: &[T]) -> &'tcx List { assert!(!mem::needs_drop::()); @@ -73,7 +91,7 @@ impl List { .cast::() .copy_from_nonoverlapping(slice.as_ptr(), slice.len()); - &mut *mem + &*mem } } @@ -107,11 +125,24 @@ impl> Encodable for &List { } } +impl PartialEq for List { + #[inline] + fn eq(&self, other: &List) -> bool { + // Pointer equality implies list equality (due to the unique contents + // assumption). + ptr::eq(self, other) + } +} + +impl Eq for List {} + impl Ord for List where T: Ord, { fn cmp(&self, other: &List) -> Ordering { + // Pointer equality implies list equality (due to the unique contents + // assumption), but the contents must be compared otherwise. if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) } } } @@ -121,6 +152,8 @@ where T: PartialOrd, { fn partial_cmp(&self, other: &List) -> Option { + // Pointer equality implies list equality (due to the unique contents + // assumption), but the contents must be compared otherwise. if self == other { Some(Ordering::Equal) } else { @@ -129,17 +162,11 @@ where } } -impl PartialEq for List { - #[inline] - fn eq(&self, other: &List) -> bool { - ptr::eq(self, other) - } -} -impl Eq for List {} - impl Hash for List { #[inline] fn hash(&self, s: &mut H) { + // Pointer hashing is sufficient (due to the unique contents + // assumption). (self as *const List).hash(s) } } @@ -168,13 +195,24 @@ impl<'a, T: Copy> IntoIterator for &'a List { } } -impl List { - #[inline(always)] - pub fn empty<'a>() -> &'a List { - #[repr(align(64), C)] - struct EmptySlice([u8; 64]); - static EMPTY_SLICE: EmptySlice = EmptySlice([0; 64]); - assert!(mem::align_of::() <= 64); - unsafe { &*(&EMPTY_SLICE as *const _ as *const List) } +unsafe impl Sync for List {} + +unsafe impl<'a, T: 'a> rustc_data_structures::tagged_ptr::Pointer for &'a List { + const BITS: usize = std::mem::align_of::().trailing_zeros() as usize; + + #[inline] + fn into_usize(self) -> usize { + self as *const List as usize + } + + #[inline] + unsafe fn from_usize(ptr: usize) -> &'a List { + &*(ptr as *const List) + } + + unsafe fn with_ref R>(ptr: usize, f: F) -> R { + // `Self` is `&'a List` which impls `Copy`, so this is fine. + let ptr = Self::from_usize(ptr); + f(&ptr) } } From c78fb62255ea3433baa7612c968065eb4770bcbf Mon Sep 17 00:00:00 2001 From: Andrew Banchich Date: Thu, 9 Dec 2021 10:41:38 -0500 Subject: [PATCH 8/9] Improve std::iter::zip example. Update library/core/src/iter/adapters/zip.rs Co-authored-by: r00ster Update library/core/src/iter/adapters/zip.rs Co-authored-by: r00ster --- library/core/src/iter/adapters/zip.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 4b89bc363268d..920fa59c8ad6e 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -45,15 +45,23 @@ impl Zip { /// /// let xs = [1, 2, 3]; /// let ys = [4, 5, 6]; -/// for (x, y) in zip(&xs, &ys) { -/// println!("x:{}, y:{}", x, y); -/// } +/// +/// let mut iter = zip(xs, ys); +/// +/// assert_eq!(iter.next().unwrap(), (1, 4)); +/// assert_eq!(iter.next().unwrap(), (2, 5)); +/// assert_eq!(iter.next().unwrap(), (3, 6)); +/// assert!(iter.next().is_none()); /// /// // Nested zips are also possible: /// let zs = [7, 8, 9]; -/// for ((x, y), z) in zip(zip(&xs, &ys), &zs) { -/// println!("x:{}, y:{}, z:{}", x, y, z); -/// } +/// +/// let mut iter = zip(zip(xs, ys), zs); +/// +/// assert_eq!(iter.next().unwrap(), ((1, 4), 7)); +/// assert_eq!(iter.next().unwrap(), ((2, 5), 8)); +/// assert_eq!(iter.next().unwrap(), ((3, 6), 9)); +/// assert!(iter.next().is_none()); /// ``` #[unstable(feature = "iter_zip", issue = "83574")] pub fn zip(a: A, b: B) -> Zip From 8716f2780e067c3590a6be4545cf40d0b6d8865a Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 7 Dec 2021 23:46:38 +0000 Subject: [PATCH 9/9] asm: Allow using r9 (ARM) and x18 (AArch64) if they are not reserved by the current target. --- compiler/rustc_ast_lowering/src/asm.rs | 7 +++- .../rustc_codegen_ssa/src/target_features.rs | 1 + compiler/rustc_target/src/asm/aarch64.rs | 20 +++++++++-- compiler/rustc_target/src/asm/arm.rs | 19 +++++++++-- compiler/rustc_target/src/asm/mod.rs | 34 ++++++++++++++++--- 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 9f27ace25ab4b..9c28f3c7f5899 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -64,7 +64,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut clobber_abis = FxHashMap::default(); if let Some(asm_arch) = asm_arch { for (abi_name, abi_span) in &asm.clobber_abis { - match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, *abi_name) { + match asm::InlineAsmClobberAbi::parse( + asm_arch, + |feature| self.sess.target_features.contains(&Symbol::intern(feature)), + &self.sess.target, + *abi_name, + ) { Ok(abi) => { // If the abi was already in the list, emit an error match clobber_abis.get(&abi) { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index ba72e3cfafce2..63cc6faf9ec5e 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -36,6 +36,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option)] = &[ // #[target_feature]. ("thumb-mode", Some(sym::arm_target_feature)), ("thumb2", Some(sym::arm_target_feature)), + ("reserve-r9", Some(sym::arm_target_feature)), ]; const AARCH64_ALLOWED_FEATURES: &[(&str, Option)] = &[ diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 76e50678314ae..4bf909ce46d25 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,4 +1,5 @@ use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::Target; use rustc_macros::HashStable_Generic; use std::fmt; @@ -70,6 +71,22 @@ impl AArch64InlineAsmRegClass { } } +pub fn reserved_x18( + _arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + target: &Target, +) -> Result<(), &'static str> { + if target.os == "android" + || target.is_like_fuchsia + || target.is_like_osx + || target.is_like_windows + { + Err("x18 is a reserved register on this target") + } else { + Ok(()) + } +} + def_regs! { AArch64 AArch64InlineAsmReg AArch64InlineAsmRegClass { x0: reg = ["x0", "w0"], @@ -90,6 +107,7 @@ def_regs! { x15: reg = ["x15", "w15"], x16: reg = ["x16", "w16"], x17: reg = ["x17", "w17"], + x18: reg = ["x18", "w18"] % reserved_x18, x20: reg = ["x20", "w20"], x21: reg = ["x21", "w21"], x22: reg = ["x22", "w22"], @@ -149,8 +167,6 @@ def_regs! { p14: preg = ["p14"], p15: preg = ["p15"], ffr: preg = ["ffr"], - #error = ["x18", "w18"] => - "x18 is used as a reserved register on some targets and cannot be used as an operand for inline asm", #error = ["x19", "w19"] => "x19 is used internally by LLVM and cannot be used as an operand for inline asm", #error = ["x29", "w29", "fp", "wfp"] => diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 4eeb7fcc71bce..b03594b3151a3 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -99,6 +99,22 @@ fn not_thumb1( } } +fn reserved_r9( + arch: InlineAsmArch, + mut has_feature: impl FnMut(&str) -> bool, + target: &Target, +) -> Result<(), &'static str> { + not_thumb1(arch, &mut has_feature, target)?; + + // We detect this using the reserved-r9 feature instead of using the target + // because the relocation model can be changed with compiler options. + if has_feature("reserved-r9") { + Err("the RWPI static base register (r9) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + def_regs! { Arm ArmInlineAsmReg ArmInlineAsmRegClass { r0: reg = ["r0", "a1"], @@ -109,6 +125,7 @@ def_regs! { r5: reg = ["r5", "v2"], r7: reg = ["r7", "v4"] % frame_pointer_r7, r8: reg = ["r8", "v5"] % not_thumb1, + r9: reg = ["r9", "v6", "rfp"] % reserved_r9, r10: reg = ["r10", "sl"] % not_thumb1, r11: reg = ["r11", "fp"] % frame_pointer_r11, r12: reg = ["r12", "ip"] % not_thumb1, @@ -195,8 +212,6 @@ def_regs! { q15: qreg = ["q15"], #error = ["r6", "v3"] => "r6 is used internally by LLVM and cannot be used as an operand for inline asm", - #error = ["r9", "v6", "rfp"] => - "r9 is used internally by LLVM and cannot be used as an operand for inline asm", #error = ["r13", "sp"] => "the stack pointer cannot be used as an operand for inline asm", #error = ["r15", "pc"] => diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index cf940594bc459..f1f5f4389e3e9 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -785,6 +785,7 @@ pub enum InlineAsmClobberAbi { X86_64SysV, Arm, AArch64, + AArch64NoX18, RiscV, } @@ -793,6 +794,7 @@ impl InlineAsmClobberAbi { /// clobber ABIs for the target. pub fn parse( arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, target: &Target, name: Symbol, ) -> Result { @@ -816,7 +818,13 @@ impl InlineAsmClobberAbi { _ => Err(&["C", "system", "efiapi", "aapcs"]), }, InlineAsmArch::AArch64 => match name { - "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::AArch64), + "C" | "system" | "efiapi" => { + Ok(if aarch64::reserved_x18(arch, has_feature, target).is_err() { + InlineAsmClobberAbi::AArch64NoX18 + } else { + InlineAsmClobberAbi::AArch64 + }) + } _ => Err(&["C", "system", "efiapi"]), }, InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { @@ -891,8 +899,25 @@ impl InlineAsmClobberAbi { AArch64 AArch64InlineAsmReg { x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, - // x18 is platform-reserved or temporary, but we exclude it - // here since it is a reserved register. + x16, x17, x18, x30, + + // Technically the low 64 bits of v8-v15 are preserved, but + // we have no way of expressing this using clobbers. + v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, + + p0, p1, p2, p3, p4, p5, p6, p7, + p8, p9, p10, p11, p12, p13, p14, p15, + ffr, + + } + }, + InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! { + AArch64 AArch64InlineAsmReg { + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x30, // Technically the low 64 bits of v8-v15 are preserved, but @@ -910,7 +935,8 @@ impl InlineAsmClobberAbi { }, InlineAsmClobberAbi::Arm => clobbered_regs! { Arm ArmInlineAsmReg { - // r9 is platform-reserved and is treated as callee-saved. + // r9 is either platform-reserved or callee-saved. Either + // way we don't need to clobber it. r0, r1, r2, r3, r12, r14, // The finest-grained register variant is used here so that