diff --git a/RELEASES.md b/RELEASES.md index a6db2c5e9e99c..85570c9ac73ef 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,13 @@ +Version 1.72.1 (2023-09-14) +=========================== + +- [Adjust codegen change to improve LLVM codegen](https://github.com/rust-lang/rust/pull/115236) +- [rustdoc: Fix self ty params in objects with lifetimes](https://github.com/rust-lang/rust/pull/115276) +- [Fix regression in compile times](https://github.com/rust-lang/rust/pull/114948) +- Resolve some ICE regressions in the compiler: + - [#115215](https://github.com/rust-lang/rust/pull/115215) + - [#115559](https://github.com/rust-lang/rust/pull/115559) + Version 1.72.0 (2023-08-24) ========================== diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 58e97be34f280..7f7da8483ecdc 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -431,7 +431,11 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { // Vectors, even for non-power-of-two sizes, have the same layout as // arrays but don't count as aggregate types + // While LLVM theoretically supports non-power-of-two sizes, and they + // often work fine, sometimes x86-isel deals with them horribly + // (see #115212) so for now only use power-of-two ones. if let FieldsShape::Array { count, .. } = self.layout.fields() + && count.is_power_of_two() && let element = self.field(cx, 0) && element.ty.is_integral() { diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs index da8bf5e7916a7..378c540132207 100644 --- a/compiler/rustc_codegen_ssa/src/mir/locals.rs +++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs @@ -7,7 +7,6 @@ use rustc_index::IndexVec; use rustc_middle::mir; use rustc_middle::ty::print::with_no_trimmed_paths; use std::ops::{Index, IndexMut}; - pub(super) struct Locals<'tcx, V> { values: IndexVec>, } @@ -36,17 +35,18 @@ impl<'tcx, V> Locals<'tcx, V> { impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub(super) fn initialize_locals(&mut self, values: Vec>) { assert!(self.locals.values.is_empty()); - + // FIXME(#115215): After #115025 get's merged this might not be necessary for (local, value) in values.into_iter().enumerate() { match value { LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (), LocalRef::Operand(op) => { let local = mir::Local::from_usize(local); let expected_ty = self.monomorphize(self.mir.local_decls[local].ty); - assert_eq!(expected_ty, op.layout.ty, "unexpected initial operand type"); + if expected_ty != op.layout.ty { + warn!("Unexpected initial operand type. See the issues/114858"); + } } } - self.locals.values.push(value); } } diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index a133c9d4782c8..e782c037376f6 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -203,7 +203,12 @@ pub fn deduced_param_attrs<'tcx>( body.local_decls.iter().skip(1).take(body.arg_count).enumerate().map( |(arg_index, local_decl)| DeducedParamAttrs { read_only: !deduce_read_only.mutable_args.contains(arg_index) - && local_decl.ty.is_freeze(tcx, param_env), + // We must normalize here to reveal opaques and normalize + // their substs, otherwise we'll see exponential blow-up in + // compile times: #113372 + && tcx + .normalize_erasing_regions(param_env, local_decl.ty) + .is_freeze(tcx, param_env), }, ), ); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index ff55eaf13ad5c..ae6fc7cf8f825 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -57,16 +57,12 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { let ty = OpportunisticRegionResolver::new(self).fold_ty(ty); // We do not expect existential variables in implied bounds. - // We may however encounter unconstrained lifetime variables in invalid - // code. See #110161 for context. + // We may however encounter unconstrained lifetime variables + // in very rare cases. + // + // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for + // an example. assert!(!ty.has_non_region_infer()); - if ty.has_infer() { - self.tcx.sess.delay_span_bug( - self.tcx.def_span(body_id), - "skipped implied_outlives_bounds due to unconstrained lifetimes", - ); - return vec![]; - } let mut canonical_var_values = OriginalQueryValues::default(); let canonical_ty = diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0182d50773d77..8ad1ed0959762 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -167,8 +167,7 @@ fn clean_generic_bound<'tcx>( let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(cx.tcx, def_id)); let generic_args = clean_generic_args(generic_args, cx); - let GenericArgs::AngleBracketed { bindings, .. } = generic_args - else { + let GenericArgs::AngleBracketed { bindings, .. } = generic_args else { bug!("clean: parenthesized `GenericBound::LangItemTrait`"); }; @@ -1818,33 +1817,46 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( #[derive(Debug)] pub(crate) enum ContainerTy<'tcx> { Ref(ty::Region<'tcx>), - Regular { ty: DefId, substs: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, arg: usize }, + Regular { + ty: DefId, + args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, + has_self: bool, + arg: usize, + }, } impl<'tcx> ContainerTy<'tcx> { fn object_lifetime_default(self, tcx: TyCtxt<'tcx>) -> ObjectLifetimeDefault<'tcx> { match self { Self::Ref(region) => ObjectLifetimeDefault::Arg(region), - Self::Regular { ty: container, substs, arg: index } => { + Self::Regular { ty: container, args, has_self, arg: index } => { let (DefKind::Struct | DefKind::Union | DefKind::Enum - | DefKind::TyAlias - | DefKind::Trait - | DefKind::AssocTy - | DefKind::Variant) = tcx.def_kind(container) + | DefKind::TyAlias { .. } + | DefKind::Trait) = tcx.def_kind(container) else { return ObjectLifetimeDefault::Empty; }; let generics = tcx.generics_of(container); - let param = generics.params[index].def_id; - let default = tcx.object_lifetime_default(param); + debug_assert_eq!(generics.parent_count, 0); + + // If the container is a trait object type, the arguments won't contain the self type but the + // generics of the corresponding trait will. In such a case, offset the index by one. + // For comparison, if the container is a trait inside a bound, the arguments do contain the + // self type. + let offset = + if !has_self && generics.parent.is_none() && generics.has_self { 1 } else { 0 }; + let param = generics.params[index + offset].def_id; + let default = tcx.object_lifetime_default(param); match default { rbv::ObjectLifetimeDefault::Param(lifetime) => { + // The index is relative to the parent generics but since we don't have any, + // we don't need to translate it. let index = generics.param_def_id_to_index[&lifetime]; - let arg = substs.skip_binder()[index as usize].expect_region(); + let arg = args.skip_binder()[index as usize].expect_region(); ObjectLifetimeDefault::Arg(arg) } rbv::ObjectLifetimeDefault::Empty => ObjectLifetimeDefault::Empty, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index f375f0efbd17b..b786ecbe38f87 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -54,8 +54,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { let primitives = local_crate.primitives(cx.tcx); let keywords = local_crate.keywords(cx.tcx); { - let ItemKind::ModuleItem(ref mut m) = *module.kind - else { unreachable!() }; + let ItemKind::ModuleItem(ref mut m) = *module.kind else { unreachable!() }; m.items.extend(primitives.iter().map(|&(def_id, prim)| { Item::from_def_id_and_parts( def_id, @@ -74,18 +73,15 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { pub(crate) fn substs_to_args<'tcx>( cx: &mut DocContext<'tcx>, - substs: ty::Binder<'tcx, &'tcx [ty::subst::GenericArg<'tcx>]>, - mut skip_first: bool, + args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, + has_self: bool, container: Option, ) -> Vec { + let mut skip_first = has_self; let mut ret_val = - Vec::with_capacity(substs.skip_binder().len().saturating_sub(if skip_first { - 1 - } else { - 0 - })); + Vec::with_capacity(args.skip_binder().len().saturating_sub(if skip_first { 1 } else { 0 })); - ret_val.extend(substs.iter().enumerate().filter_map(|(index, kind)| { + ret_val.extend(args.iter().enumerate().filter_map(|(index, kind)| { match kind.skip_binder().unpack() { GenericArgKind::Lifetime(lt) => { Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) @@ -100,7 +96,8 @@ pub(crate) fn substs_to_args<'tcx>( None, container.map(|container| crate::clean::ContainerTy::Regular { ty: container, - substs, + args, + has_self, arg: index, }), ))), diff --git a/src/version b/src/version index 0834888f55808..22d6771a47d93 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.72.0 +1.72.1 diff --git a/tests/codegen/mem-replace-simple-type.rs b/tests/codegen/mem-replace-simple-type.rs index 6151177de15b2..b5e4fbd905168 100644 --- a/tests/codegen/mem-replace-simple-type.rs +++ b/tests/codegen/mem-replace-simple-type.rs @@ -34,12 +34,21 @@ pub fn replace_ref_str<'a>(r: &mut &'a str, v: &'a str) -> &'a str { } #[no_mangle] -// CHECK-LABEL: @replace_short_array( -pub fn replace_short_array(r: &mut [u32; 3], v: [u32; 3]) -> [u32; 3] { +// CHECK-LABEL: @replace_short_array_3( +pub fn replace_short_array_3(r: &mut [u32; 3], v: [u32; 3]) -> [u32; 3] { // CHECK-NOT: alloca - // CHECK: %[[R:.+]] = load <3 x i32>, ptr %r, align 4 - // CHECK: store <3 x i32> %[[R]], ptr %0 - // CHECK: %[[V:.+]] = load <3 x i32>, ptr %v, align 4 - // CHECK: store <3 x i32> %[[V]], ptr %r + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %result, ptr align 4 %r, i64 12, i1 false) + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %r, ptr align 4 %v, i64 12, i1 false) + std::mem::replace(r, v) +} + +#[no_mangle] +// CHECK-LABEL: @replace_short_array_4( +pub fn replace_short_array_4(r: &mut [u32; 4], v: [u32; 4]) -> [u32; 4] { + // CHECK-NOT: alloca + // CHECK: %[[R:.+]] = load <4 x i32>, ptr %r, align 4 + // CHECK: store <4 x i32> %[[R]], ptr %result + // CHECK: %[[V:.+]] = load <4 x i32>, ptr %v, align 4 + // CHECK: store <4 x i32> %[[V]], ptr %r std::mem::replace(r, v) } diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs index 419645a3fc6bc..27bc00bc3abb8 100644 --- a/tests/codegen/swap-small-types.rs +++ b/tests/codegen/swap-small-types.rs @@ -11,11 +11,12 @@ type RGB48 = [u16; 3]; // CHECK-LABEL: @swap_rgb48_manually( #[no_mangle] pub fn swap_rgb48_manually(x: &mut RGB48, y: &mut RGB48) { - // CHECK-NOT: alloca - // CHECK: %[[TEMP0:.+]] = load <3 x i16>, ptr %x, align 2 - // CHECK: %[[TEMP1:.+]] = load <3 x i16>, ptr %y, align 2 - // CHECK: store <3 x i16> %[[TEMP1]], ptr %x, align 2 - // CHECK: store <3 x i16> %[[TEMP0]], ptr %y, align 2 + // FIXME: See #115212 for why this has an alloca again + + // CHECK: alloca [3 x i16], align 2 + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 6, i1 false) + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 6, i1 false) + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 6, i1 false) let temp = *x; *x = *y; @@ -25,11 +26,25 @@ pub fn swap_rgb48_manually(x: &mut RGB48, y: &mut RGB48) { // CHECK-LABEL: @swap_rgb48 #[no_mangle] pub fn swap_rgb48(x: &mut RGB48, y: &mut RGB48) { + // FIXME: See #115212 for why this has an alloca again + + // CHECK: alloca [3 x i16], align 2 + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 6, i1 false) + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 6, i1 false) + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 6, i1 false) + swap(x, y) +} + +type RGBA64 = [u16; 4]; + +// CHECK-LABEL: @swap_rgba64 +#[no_mangle] +pub fn swap_rgba64(x: &mut RGBA64, y: &mut RGBA64) { // CHECK-NOT: alloca - // CHECK: load <3 x i16> - // CHECK: load <3 x i16> - // CHECK: store <3 x i16> - // CHECK: store <3 x i16> + // CHECK-DAG: %[[XVAL:.+]] = load <4 x i16>, ptr %x, align 2 + // CHECK-DAG: %[[YVAL:.+]] = load <4 x i16>, ptr %y, align 2 + // CHECK-DAG: store <4 x i16> %[[YVAL]], ptr %x, align 2 + // CHECK-DAG: store <4 x i16> %[[XVAL]], ptr %y, align 2 swap(x, y) } diff --git a/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs b/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs index 644d0699e9d07..df88530071be7 100644 --- a/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs +++ b/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs @@ -65,3 +65,22 @@ pub trait HigherRankedBoundTrait1<'e> where for<'l> Self: 'e + 'l {} pub trait AmbiguousBoundTrait<'a, 'b>: 'a + 'b {} pub struct AmbiguousBoundWrapper<'a, 'b, T: ?Sized + 'a + 'b>(&'a T, &'b T); + +// Trait objects inside of another trait object, a trait bound or an associated type. + +pub trait Inner {} +pub trait Outer {} +pub trait Base { + type Type; +} +impl Base for () { + type Type = (); +} + +pub type NestedTraitObjects = dyn Outer; + +pub fn apit_rpit(o: impl Outer) -> impl Outer { + o +} + +pub type AssocTy = <() as Base>::Type; diff --git a/tests/rustdoc/inline_cross/dyn_trait.rs b/tests/rustdoc/inline_cross/dyn_trait.rs index 1de01af83d13e..679972f035ad4 100644 --- a/tests/rustdoc/inline_cross/dyn_trait.rs +++ b/tests/rustdoc/inline_cross/dyn_trait.rs @@ -128,3 +128,18 @@ pub use dyn_trait::BareAmbiguousBoundEarly1; // @has user/type.BareAmbiguousBoundStatic.html // @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'o, 'o> + 'static;" pub use dyn_trait::BareAmbiguousBoundStatic; + +// Regression test for issue #115179: + +// @has user/type.NestedTraitObjects.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Outer;" +pub use dyn_trait::NestedTraitObjects; + +// @has user/fn.apit_rpit.html +// @has - '//pre[@class="rust item-decl"]' \ +// "apit_rpit(o: impl Outer) -> impl Outer" +pub use dyn_trait::apit_rpit; + +// @has user/type.AssocTy.html +// @has - '//*[@class="rust item-decl"]//code' "<() as Base>::Type" +pub use dyn_trait::AssocTy; diff --git a/tests/ui/async-await/deep-futures-are-freeze.rs b/tests/ui/async-await/deep-futures-are-freeze.rs new file mode 100644 index 0000000000000..dd676d5e18c02 --- /dev/null +++ b/tests/ui/async-await/deep-futures-are-freeze.rs @@ -0,0 +1,179 @@ +// build-pass +// compile-flags: -Copt-level=s -Clto=fat +// no-prefer-dynamic +// edition: 2021 + +#![recursion_limit = "256"] + +fn main() { + spawn(move || main0()) +} + +fn spawn(future: impl FnOnce() -> F) { + future(); +} + +async fn main0() { + main1().await; + main2().await; +} +async fn main1() { + main2().await; + main3().await; +} +async fn main2() { + main3().await; + main4().await; +} +async fn main3() { + main4().await; + main5().await; +} +async fn main4() { + main5().await; + main6().await; +} +async fn main5() { + main6().await; + main7().await; +} +async fn main6() { + main7().await; + main8().await; +} +async fn main7() { + main8().await; + main9().await; +} +async fn main8() { + main9().await; + main10().await; +} +async fn main9() { + main10().await; + main11().await; +} +async fn main10() { + main11().await; + main12().await; +} +async fn main11() { + main12().await; + main13().await; +} +async fn main12() { + main13().await; + main14().await; +} +async fn main13() { + main14().await; + main15().await; +} +async fn main14() { + main15().await; + main16().await; +} +async fn main15() { + main16().await; + main17().await; +} +async fn main16() { + main17().await; + main18().await; +} +async fn main17() { + main18().await; + main19().await; +} +async fn main18() { + main19().await; + main20().await; +} +async fn main19() { + main20().await; + main21().await; +} +async fn main20() { + main21().await; + main22().await; +} +async fn main21() { + main22().await; + main23().await; +} +async fn main22() { + main23().await; + main24().await; +} +async fn main23() { + main24().await; + main25().await; +} +async fn main24() { + main25().await; + main26().await; +} +async fn main25() { + main26().await; + main27().await; +} +async fn main26() { + main27().await; + main28().await; +} +async fn main27() { + main28().await; + main29().await; +} +async fn main28() { + main29().await; + main30().await; +} +async fn main29() { + main30().await; + main31().await; +} +async fn main30() { + main31().await; + main32().await; +} +async fn main31() { + main32().await; + main33().await; +} +async fn main32() { + main33().await; + main34().await; +} +async fn main33() { + main34().await; + main35().await; +} +async fn main34() { + main35().await; + main36().await; +} +async fn main35() { + main36().await; + main37().await; +} +async fn main36() { + main37().await; + main38().await; +} +async fn main37() { + main38().await; + main39().await; +} +async fn main38() { + main39().await; + main40().await; +} +async fn main39() { + main40().await; +} +async fn main40() { + boom(&mut ()).await; +} + +async fn boom(f: &mut ()) {} diff --git a/tests/ui/codegen/subtyping-enforces-type-equality.rs b/tests/ui/codegen/subtyping-enforces-type-equality.rs new file mode 100644 index 0000000000000..a5ffcb3f85496 --- /dev/null +++ b/tests/ui/codegen/subtyping-enforces-type-equality.rs @@ -0,0 +1,48 @@ +// ignore-pass +// build-pass +// edition:2021 +use std::future::Future; +use std::pin::Pin; + +type BoxFuture = Pin>>; + +fn main() { + let _ = wrapper_call(handler); +} + +async fn wrapper_call(handler: impl Handler) { + handler.call().await; +} +async fn handler() { + f(&()).await; +} +async fn f<'a>(db: impl Acquire<'a>) { + db.acquire().await; +} + +trait Handler { + type Future: Future; + fn call(self) -> Self::Future; +} + +impl Handler for F +where + F: Fn() -> Fut, + Fut: Future, +{ + type Future = Fut; + fn call(self) -> Self::Future { + loop {} + } +} + +trait Acquire<'a> { + type Connection; + fn acquire(self) -> BoxFuture; +} +impl<'a> Acquire<'a> for &'a () { + type Connection = Self; + fn acquire(self) -> BoxFuture { + loop {} + } +} diff --git a/tests/ui/codegen/subtyping-enforces-type-equality.stderr b/tests/ui/codegen/subtyping-enforces-type-equality.stderr new file mode 100644 index 0000000000000..870ca0f839fee --- /dev/null +++ b/tests/ui/codegen/subtyping-enforces-type-equality.stderr @@ -0,0 +1 @@ +WARN rustc_codegen_ssa::mir::locals Unexpected initial operand type. See the issues/114858 diff --git a/tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs b/tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs new file mode 100644 index 0000000000000..025e5176ff7ee --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-unconstrained-1.rs @@ -0,0 +1,28 @@ +// check-pass + +// Regression test for #112832. +pub trait QueryDb { + type Db; +} + +pub struct QueryTable { + db: DB, + storage: Q, +} + +// We normalize `::Db` to `>::SendDb` +// using the where-bound. 'd is an unconstrained region variable which previously +// triggered an assert. +impl QueryTable::Db> where Q: for<'d> AsyncQueryFunction<'d> {} + +pub trait AsyncQueryFunction<'d>: QueryDb>::SendDb> { + type SendDb: 'd; +} + +pub trait QueryStorageOpsAsync +where + Q: for<'d> AsyncQueryFunction<'d>, +{ +} + +fn main() {} diff --git a/tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs b/tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs new file mode 100644 index 0000000000000..976054facee5e --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-unconstrained-2.rs @@ -0,0 +1,20 @@ +// check-pass + +// Another minimized regression test for #112832. +trait Trait { + type Assoc; +} + +trait Sub<'a>: Trait>::SubAssoc> { + type SubAssoc; +} + +// By using the where-clause we normalize `::Assoc` to +// `>::SubAssoc` where `'a` is an unconstrained region +// variable. +fn foo(x: ::Assoc) +where + for<'a> T: Sub<'a>, +{} + +fn main() {}