From 263a0dec60c8cf1917b65abc83f7dae29086e986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Aug 2023 01:03:31 +0200 Subject: [PATCH] Compute variances for lazy type aliases --- .../rustc_hir_analysis/src/check/wfcheck.rs | 26 +++++++++++-- .../src/variance/constraints.rs | 28 ++++++++++++-- .../rustc_hir_analysis/src/variance/mod.rs | 10 ++++- .../rustc_hir_analysis/src/variance/terms.rs | 8 +++- compiler/rustc_metadata/src/rmeta/encoder.rs | 10 +++-- compiler/rustc_middle/src/query/mod.rs | 2 +- tests/ui/lazy-type-alias/variance.rs | 38 +++++++++++++++++++ 7 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 tests/ui/lazy-type-alias/variance.rs diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e64848da86b..16d4d099c7e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -245,13 +245,14 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::TyAlias(hir_ty, ..) => { + hir::ItemKind::TyAlias(hir_ty, ast_generics) => { if tcx.features().lazy_type_alias || tcx.type_of(item.owner_id).skip_binder().has_opaque_types() { // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); + check_variances_for_type_defn(tcx, item, ast_generics); } } _ => {} @@ -1700,10 +1701,27 @@ fn check_variances_for_type_defn<'tcx>( hir_generics: &hir::Generics<'_>, ) { let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id); - for field in tcx.adt_def(item.owner_id).all_fields() { - if field.ty(tcx, identity_args).references_error() { - return; + + match item.kind { + ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { + for field in tcx.adt_def(item.owner_id).all_fields() { + if field.ty(tcx, identity_args).references_error() { + return; + } + } } + ItemKind::TyAlias(..) => { + let ty = tcx.type_of(item.owner_id).instantiate_identity(); + + if tcx.features().lazy_type_alias || ty.has_opaque_types() { + if ty.references_error() { + return; + } + } else { + bug!(); + } + } + _ => bug!(), } let ty_predicates = tcx.predicates_of(item.owner_id); diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index ec8889781f4..4a3d522e488 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -6,7 +6,7 @@ use hir::def_id::{DefId, LocalDefId}; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use super::terms::VarianceTerm::*; @@ -78,6 +78,12 @@ pub fn add_constraints_from_crate<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id), + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => + { + constraint_cx.build_constraints_for_item(def_id) + } _ => {} } } @@ -101,7 +107,18 @@ fn build_constraints_for_item(&mut self, def_id: LocalDefId) { let inferred_start = self.terms_cx.inferred_starts[&def_id]; let current_item = &CurrentItem { inferred_start }; - match tcx.type_of(def_id).instantiate_identity().kind() { + let ty = tcx.type_of(def_id).instantiate_identity(); + + // The type as returned by `type_of` is the underlying type and generally not a weak projection. + // Therefore we need to check the `DefKind` first. + if let DefKind::TyAlias = tcx.def_kind(def_id) + && (tcx.features().lazy_type_alias || ty.has_opaque_types()) + { + self.add_constraints_from_ty(current_item, ty, self.covariant); + return; + } + + match ty.kind() { ty::Adt(def, _) => { // Not entirely obvious: constraints on structs/enums do not // affect the variance of their type parameters. See discussion @@ -127,6 +144,7 @@ fn build_constraints_for_item(&mut self, def_id: LocalDefId) { } ty::Error(_) => {} + _ => { span_bug!( tcx.def_span(def_id), @@ -252,10 +270,14 @@ fn add_constraints_from_ty( self.add_constraints_from_args(current, def.did(), args, variance); } - ty::Alias(_, ref data) => { + ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, ref data) => { self.add_constraints_from_invariant_args(current, data.args, variance); } + ty::Alias(ty::Weak, ref data) => { + self.add_constraints_from_args(current, data.def_id, data.args, variance); + } + ty::Dynamic(data, r, _) => { // The type `dyn Trait +'a` is covariant w/r/t `'a`: self.add_constraints_from_region(current, r, variance); diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 6952a3fa66f..2ef294c6793 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, CrateVariancesMap, GenericArgsRef, Ty, TyCtxt}; -use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; use std::ops::ControlFlow; /// Defines the `TermsContext` basically houses an arena where we can @@ -56,6 +56,14 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { let crate_map = tcx.crate_variances(()); return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); } + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(item_def_id).instantiate_identity().has_opaque_types() => + { + // These are inferred. + let crate_map = tcx.crate_variances(()); + return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); + } DefKind::OpaqueTy => { return variance_of_opaque(tcx, item_def_id); } diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index ed03c5da26f..1ef3d383bd8 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -12,7 +12,7 @@ use rustc_arena::DroplessArena; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use std::fmt; use self::VarianceTerm::*; @@ -97,6 +97,12 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id), + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => + { + terms_cx.add_inferreds_for_item(def_id) + } _ => {} } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d72053ca985..f12094a271f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -30,6 +30,7 @@ use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AssocItemContainer, SymbolName, Ty, TyCtxt}; use rustc_middle::util::common::to_readable_str; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; @@ -1034,7 +1035,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { } } -fn should_encode_variances(def_kind: DefKind) -> bool { +fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: DefKind) -> bool { match def_kind { DefKind::Struct | DefKind::Union @@ -1053,7 +1054,6 @@ fn should_encode_variances(def_kind: DefKind) -> bool { | DefKind::Static(..) | DefKind::Const | DefKind::ForeignMod - | DefKind::TyAlias | DefKind::Impl { .. } | DefKind::Trait | DefKind::TraitAlias @@ -1067,6 +1067,10 @@ fn should_encode_variances(def_kind: DefKind) -> bool { | DefKind::Closure | DefKind::Generator | DefKind::ExternCrate => false, + DefKind::TyAlias => { + tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() + } } } @@ -1349,7 +1353,7 @@ fn encode_def_ids(&mut self) { self.encode_default_body_stability(def_id); self.encode_deprecation(def_id); } - if should_encode_variances(def_kind) { + if should_encode_variances(tcx, def_id, def_kind) { let v = self.tcx.variances_of(def_id); record_array!(self.tables.variances_of[def_id] <- v); } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1189dcdfcbb..a02f9a9f796 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -749,7 +749,7 @@ separate_provide_extern } - /// Gets a map with the variance of every item; use `item_variance` instead. + /// Gets a map with the variance of every item; use `variances_of` instead. query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> { arena_cache desc { "computing the variances for items in this crate" } diff --git a/tests/ui/lazy-type-alias/variance.rs b/tests/ui/lazy-type-alias/variance.rs new file mode 100644 index 00000000000..f83215856b8 --- /dev/null +++ b/tests/ui/lazy-type-alias/variance.rs @@ -0,0 +1,38 @@ +// This is a regression test for issue #114221. +// Check that we compute variances for lazy type aliases. + +// check-pass + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +// [+] `A` is covariant over `'a`. +struct A<'a>(Co<'a>); + +// [+] `Co` is covariant over `'a`. +type Co<'a> = &'a (); + +fn co<'a>(x: A<'static>) { + let _: A<'a> = x; +} + +// [-] `B` is contravariant over `'a`. +struct B<'a>(Contra<'a>); + +// [-] `Contra` is contravariant over `'a`. +type Contra<'a> = fn(&'a ()); + +fn contra<'a>(x: B<'a>) { + let _: B<'static> = x; +} + +struct C(CoContra); + +// [+, -] `CoContra` is covariant over `T` and contravariant over `U`. +type CoContra = Option<(T, fn(U))>; + +fn co_contra<'a>(x: C<&'static (), &'a ()>) -> C<&'a (), &'static ()> { + x +} + +fn main() {}