summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/type_check
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck/src/type_check')
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs84
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs19
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs127
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs115
7 files changed, 155 insertions, 196 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index 3617bf58b..11729e2c8 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -1,13 +1,13 @@
use std::fmt;
-use rustc_infer::infer::canonical::Canonical;
-use rustc_infer::traits::query::NoSolution;
+use rustc_infer::infer::{canonical::Canonical, InferOk};
use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::ty::{self, ToPredicate, TypeFoldable};
+use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable};
use rustc_span::def_id::DefId;
use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
-use rustc_trait_selection::traits::query::Fallible;
+use rustc_trait_selection::traits::query::{Fallible, NoSolution};
+use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
use crate::diagnostics::{ToUniverseInfo, UniverseInfo};
@@ -107,11 +107,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
instantiated_predicates: ty::InstantiatedPredicates<'tcx>,
locations: Locations,
) {
- for (predicate, span) in instantiated_predicates
- .predicates
- .into_iter()
- .zip(instantiated_predicates.spans.into_iter())
- {
+ for (predicate, span) in instantiated_predicates {
debug!(?predicate);
let category = ConstraintCategory::Predicate(span);
let predicate = self.normalize_with_category(predicate, locations, category);
@@ -177,4 +173,74 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
value
})
}
+
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn ascribe_user_type(
+ &mut self,
+ mir_ty: Ty<'tcx>,
+ user_ty: ty::UserType<'tcx>,
+ span: Span,
+ ) {
+ // FIXME: Ideally MIR types are normalized, but this is not always true.
+ let mir_ty = self.normalize(mir_ty, Locations::All(span));
+
+ self.fully_perform_op(
+ Locations::All(span),
+ ConstraintCategory::Boring,
+ self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(mir_ty, user_ty)),
+ )
+ .unwrap_or_else(|err| {
+ span_mirbug!(
+ self,
+ span,
+ "ascribe_user_type `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`",
+ );
+ });
+ }
+
+ /// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`.
+ ///
+ /// FIXME(#104478, #104477): This is a hack for backward-compatibility.
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn ascribe_user_type_skip_wf(
+ &mut self,
+ mir_ty: Ty<'tcx>,
+ user_ty: ty::UserType<'tcx>,
+ span: Span,
+ ) {
+ let ty::UserType::Ty(user_ty) = user_ty else { bug!() };
+
+ // A fast path for a common case with closure input/output types.
+ if let ty::Infer(_) = user_ty.kind() {
+ self.eq_types(user_ty, mir_ty, Locations::All(span), ConstraintCategory::Boring)
+ .unwrap();
+ return;
+ }
+
+ let mir_ty = self.normalize(mir_ty, Locations::All(span));
+ let cause = ObligationCause::dummy_with_span(span);
+ let param_env = self.param_env;
+ let op = |infcx: &'_ _| {
+ let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let user_ty = ocx.normalize(&cause, param_env, user_ty);
+ ocx.eq(&cause, param_env, user_ty, mir_ty)?;
+ if !ocx.select_all_or_error().is_empty() {
+ return Err(NoSolution);
+ }
+ Ok(InferOk { value: (), obligations: vec![] })
+ };
+
+ self.fully_perform_op(
+ Locations::All(span),
+ ConstraintCategory::Boring,
+ type_op::custom::CustomTypeOp::new(op, || "ascribe_user_type_skip_wf".to_string()),
+ )
+ .unwrap_or_else(|err| {
+ span_mirbug!(
+ self,
+ span,
+ "ascribe_user_type_skip_wf `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`",
+ );
+ });
+ }
}
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index ce7f857e2..e15d1b99a 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -107,7 +107,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
closure_substs: ty::SubstsRef<'tcx>,
) {
// Extract the values of the free regions in `closure_substs`
- // into a vector. These are the regions that we will be
+ // into a vector. These are the regions that we will be
// relating to one another.
let closure_mapping = &UniversalRegions::closure_mapping(
self.tcx,
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 14cfc3613..82ff86247 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -85,7 +85,7 @@ impl UniversalRegionRelations<'_> {
/// outlives `fr` and (b) is not local.
///
/// (*) If there are multiple competing choices, we return all of them.
- pub(crate) fn non_local_upper_bounds<'a>(&'a self, fr: RegionVid) -> Vec<RegionVid> {
+ pub(crate) fn non_local_upper_bounds(&self, fr: RegionVid) -> Vec<RegionVid> {
debug!("non_local_upper_bound(fr={:?})", fr);
let res = self.non_local_bounds(&self.inverse_outlives, fr);
assert!(!res.is_empty(), "can't find an upper bound!?");
@@ -98,7 +98,7 @@ impl UniversalRegionRelations<'_> {
let upper_bounds = self.non_local_upper_bounds(fr);
// In case we find more than one, reduce to one for
- // convenience. This is to prevent us from generating more
+ // convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds);
@@ -128,7 +128,7 @@ impl UniversalRegionRelations<'_> {
let lower_bounds = self.non_local_bounds(&self.outlives, fr);
// In case we find more than one, reduce to one for
- // convenience. This is to prevent us from generating more
+ // convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds);
@@ -148,9 +148,9 @@ impl UniversalRegionRelations<'_> {
/// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`.
/// Repeatedly invokes `postdom_parent` until we find something that is not
/// local. Returns `None` if we never do so.
- fn non_local_bounds<'a>(
+ fn non_local_bounds(
&self,
- relation: &'a TransitiveRelation<RegionVid>,
+ relation: &TransitiveRelation<RegionVid>,
fr0: RegionVid,
) -> Vec<RegionVid> {
// This method assumes that `fr0` is one of the universally
@@ -359,14 +359,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
- OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+ OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
- }
-
- OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
- self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
+ .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
}
}
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 62c6f9581..fa9ea769a 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -10,7 +10,7 @@
use rustc_index::vec::Idx;
use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::*;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
use crate::universal_regions::UniversalRegions;
@@ -18,6 +18,52 @@ use crate::universal_regions::UniversalRegions;
use super::{Locations, TypeChecker};
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+ /// Check explicit closure signature annotation,
+ /// e.g., `|x: FxHashMap<_, &'static u32>| ...`.
+ #[instrument(skip(self, body), level = "debug")]
+ pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) {
+ let mir_def_id = body.source.def_id().expect_local();
+ if !self.tcx().is_closure(mir_def_id.to_def_id()) {
+ return;
+ }
+ let Some(user_provided_poly_sig) =
+ self.tcx().typeck(mir_def_id).user_provided_sigs.get(&mir_def_id)
+ else {
+ return;
+ };
+
+ // Instantiate the canonicalized variables from user-provided signature
+ // (e.g., the `_` in the code above) with fresh variables.
+ // Then replace the bound items in the fn sig with fresh variables,
+ // so that they represent the view from "inside" the closure.
+ let user_provided_sig = self
+ .instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig);
+ let user_provided_sig = self.infcx.replace_bound_vars_with_fresh_vars(
+ body.span,
+ LateBoundRegionConversionTime::FnCall,
+ user_provided_sig,
+ );
+
+ for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip(
+ // In MIR, closure args begin with an implicit `self`. Skip it!
+ body.args_iter().skip(1).map(|local| &body.local_decls[local]),
+ ) {
+ self.ascribe_user_type_skip_wf(
+ arg_decl.ty,
+ ty::UserType::Ty(user_ty),
+ arg_decl.source_info.span,
+ );
+ }
+
+ // If the user explicitly annotated the output type, enforce it.
+ let output_decl = &body.local_decls[RETURN_PLACE];
+ self.ascribe_user_type_skip_wf(
+ output_decl.ty,
+ ty::UserType::Ty(user_provided_sig.output()),
+ output_decl.source_info.span,
+ );
+ }
+
#[instrument(skip(self, body, universal_regions), level = "debug")]
pub(super) fn equate_inputs_and_outputs(
&mut self,
@@ -31,40 +77,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
debug!(?normalized_output_ty);
debug!(?normalized_input_tys);
- let mir_def_id = body.source.def_id().expect_local();
-
- // If the user explicitly annotated the input types, extract
- // those.
- //
- // e.g., `|x: FxHashMap<_, &'static u32>| ...`
- let user_provided_sig;
- if !self.tcx().is_closure(mir_def_id.to_def_id()) {
- user_provided_sig = None;
- } else {
- let typeck_results = self.tcx().typeck(mir_def_id);
- user_provided_sig =
- typeck_results.user_provided_sigs.get(&mir_def_id).map(|user_provided_poly_sig| {
- // Instantiate the canonicalized variables from
- // user-provided signature (e.g., the `_` in the code
- // above) with fresh variables.
- let poly_sig = self.instantiate_canonical_with_fresh_inference_vars(
- body.span,
- &user_provided_poly_sig,
- );
-
- // Replace the bound items in the fn sig with fresh
- // variables, so that they represent the view from
- // "inside" the closure.
- self.infcx.replace_bound_vars_with_fresh_vars(
- body.span,
- LateBoundRegionConversionTime::FnCall,
- poly_sig,
- )
- });
- }
-
- debug!(?normalized_input_tys, ?body.local_decls);
-
// Equate expected input tys with those in the MIR.
for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
if argument_index + 1 >= body.local_decls.len() {
@@ -87,28 +99,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
}
- if let Some(user_provided_sig) = user_provided_sig {
- for (argument_index, &user_provided_input_ty) in
- user_provided_sig.inputs().iter().enumerate()
- {
- // In MIR, closures begin an implicit `self`, so
- // argument N is stored in local N+2.
- let local = Local::new(argument_index + 2);
- let mir_input_ty = body.local_decls[local].ty;
- let mir_input_span = body.local_decls[local].source_info.span;
-
- // If the user explicitly annotated the input types, enforce those.
- let user_provided_input_ty =
- self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
-
- self.equate_normalized_input_or_output(
- user_provided_input_ty,
- mir_input_ty,
- mir_input_span,
- );
- }
- }
-
debug!(
"equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}",
body.yield_ty(),
@@ -154,29 +144,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
terr
);
};
-
- // If the user explicitly annotated the output types, enforce those.
- // Note that this only happens for closures.
- if let Some(user_provided_sig) = user_provided_sig {
- let user_provided_output_ty = user_provided_sig.output();
- let user_provided_output_ty =
- self.normalize(user_provided_output_ty, Locations::All(output_span));
- if let Err(err) = self.eq_types(
- user_provided_output_ty,
- mir_output_ty,
- Locations::All(output_span),
- ConstraintCategory::BoringNoLocation,
- ) {
- span_mirbug!(
- self,
- Location::START,
- "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
- mir_output_ty,
- user_provided_output_ty,
- err
- );
- }
- }
}
#[instrument(skip(self), level = "debug")]
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
index fda2cee43..8023ef60d 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
@@ -46,7 +46,7 @@ struct Appearance {
}
rustc_index::newtype_index! {
- pub struct AppearanceIndex { .. }
+ pub struct AppearanceIndex {}
}
impl vll::LinkElem for Appearance {
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 42b577175..3ff5d188a 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -328,7 +328,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
debug_assert!(self.drop_live_at.contains(term_point));
// Otherwise, scan backwards through the statements in the
- // block. One of them may be either a definition or use
+ // block. One of them may be either a definition or use
// live point.
let term_location = self.cx.elements.to_location(term_point);
debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,);
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 6d4ec6b72..81bd4c2a7 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -38,7 +38,6 @@ use rustc_middle::ty::{
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::VariantIdx;
-use rustc_trait_selection::traits::query::type_op;
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -197,6 +196,8 @@ pub(crate) fn type_check<'mir, 'tcx>(
}
checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output);
+ checker.check_signature_annotation(&body);
+
liveness::generate(
&mut checker,
body,
@@ -208,7 +209,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
);
translate_outlives_facts(&mut checker);
- let opaque_type_values = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+ let opaque_type_values = infcx.take_opaque_types();
let opaque_type_values = opaque_type_values
.into_iter()
@@ -391,23 +392,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
check_err(self, promoted_body, ty, promoted_ty);
}
} else {
- if let Err(terr) = self.cx.fully_perform_op(
- locations,
- ConstraintCategory::Boring,
- self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
- constant.literal.ty(),
+ self.cx.ascribe_user_type(
+ constant.literal.ty(),
+ UserType::TypeOf(
uv.def.did,
UserSubsts { substs: uv.substs, user_self_ty: None },
- )),
- ) {
- span_mirbug!(
- self,
- constant,
- "bad constant type {:?} ({:?})",
- constant,
- terr
- );
- }
+ ),
+ locations.span(&self.cx.body),
+ );
}
} else if let Some(static_def_id) = constant.check_static_ptr(tcx) {
let unnormalized_ty = tcx.type_of(static_def_id);
@@ -612,7 +604,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
let locations = location.to_locations();
for constraint in constraints.outlives().iter() {
- let mut constraint = constraint.clone();
+ let mut constraint = *constraint;
constraint.locations = locations;
if let ConstraintCategory::Return(_)
| ConstraintCategory::UseAsConst
@@ -1041,58 +1033,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
debug!(?self.user_type_annotations);
for user_annotation in self.user_type_annotations {
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
- let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
- debug!(?annotation);
- match annotation {
- UserType::Ty(mut ty) => {
- ty = self.normalize(ty, Locations::All(span));
-
- if let Err(terr) = self.eq_types(
- ty,
- inferred_ty,
- Locations::All(span),
- ConstraintCategory::BoringNoLocation,
- ) {
- span_mirbug!(
- self,
- user_annotation,
- "bad user type ({:?} = {:?}): {:?}",
- ty,
- inferred_ty,
- terr
- );
- }
-
- self.prove_predicate(
- ty::Binder::dummy(ty::PredicateKind::WellFormed(inferred_ty.into())),
- Locations::All(span),
- ConstraintCategory::TypeAnnotation,
- );
- }
- UserType::TypeOf(def_id, user_substs) => {
- if let Err(terr) = self.fully_perform_op(
- Locations::All(span),
- ConstraintCategory::BoringNoLocation,
- self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
- inferred_ty,
- def_id,
- user_substs,
- )),
- ) {
- span_mirbug!(
- self,
- user_annotation,
- "bad user type AscribeUserType({:?}, {:?} {:?}, type_of={:?}): {:?}",
- inferred_ty,
- def_id,
- user_substs,
- self.tcx().type_of(def_id),
- terr,
- );
- }
- }
- }
+ self.ascribe_user_type(inferred_ty, annotation, span);
}
}
@@ -1153,16 +1095,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
category: ConstraintCategory<'tcx>,
) -> Fallible<()> {
let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty;
+ trace!(?annotated_type);
let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);
let tcx = self.infcx.tcx;
for proj in &user_ty.projs {
+ if let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() {
+ // There is nothing that we can compare here if we go through an opaque type.
+ // We're always in its defining scope as we can otherwise not project through
+ // it, so we're constraining it anyways.
+ return Ok(());
+ }
let projected_ty = curr_projected_ty.projection_ty_core(
tcx,
self.param_env,
proj,
- |this, field, _| {
+ |this, field, ()| {
let ty = this.field_ty(tcx, field);
self.normalize(ty, locations)
},
@@ -1170,10 +1119,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
curr_projected_ty = projected_ty;
}
- debug!(
- "user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}",
- user_ty.base, annotated_type, user_ty.projs, curr_projected_ty
- );
+ trace!(?curr_projected_ty);
let ty = curr_projected_ty.ty;
self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?;
@@ -1360,25 +1306,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
}
}
- TerminatorKind::SwitchInt { discr, switch_ty, .. } => {
+ TerminatorKind::SwitchInt { discr, .. } => {
self.check_operand(discr, term_location);
- let discr_ty = discr.ty(body, tcx);
- if let Err(terr) = self.sub_types(
- discr_ty,
- *switch_ty,
- term_location.to_locations(),
- ConstraintCategory::Assignment,
- ) {
- span_mirbug!(
- self,
- term,
- "bad SwitchInt ({:?} on {:?}): {:?}",
- switch_ty,
- discr_ty,
- terr
- );
- }
+ let switch_ty = discr.ty(body, tcx);
if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() {
span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty);
}
@@ -1734,7 +1665,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) {
let tcx = self.tcx();
- // Erase the regions from `ty` to get a global type. The
+ // Erase the regions from `ty` to get a global type. The
// `Sized` bound in no way depends on precise regions, so this
// shouldn't affect `is_sized`.
let erased_ty = tcx.erase_regions(ty);