Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add TypingMode::Borrowck #138785

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -483,7 +483,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
half_open_range_patterns_in_slices,
"half-open range patterns in slices are unstable"
);
gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
gate_all!(associated_const_equality, "associated const equality is incomplete");
gate_all!(yeet_expr, "`do yeet` expression is experimental");
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
7 changes: 0 additions & 7 deletions compiler/rustc_borrowck/messages.ftl
Original file line number Diff line number Diff line change
@@ -162,13 +162,6 @@ borrowck_opaque_type_lifetime_mismatch =
.prev_lifetime_label = lifetime `{$prev}` previously used here
.note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types

borrowck_opaque_type_non_generic_param =
expected generic {$kind} parameter, found `{$ty}`
.label = {STREQ($ty, "'static") ->
[true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
*[other] this generic parameter must be used with a generic {$kind} parameter
}

borrowck_partial_var_move_by_use_in_closure =
variable {$is_partial ->
[true] partially moved
7 changes: 2 additions & 5 deletions compiler/rustc_borrowck/src/consumers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This file provides API for compiler consumers.

use rustc_hir::def_id::LocalDefId;
use rustc_index::{IndexSlice, IndexVec};
use rustc_index::IndexVec;
use rustc_middle::mir::{Body, Promoted};
use rustc_middle::ty::TyCtxt;

@@ -100,8 +100,5 @@ pub fn get_body_with_borrowck_facts(
def: LocalDefId,
options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexSlice<_, _> = &promoted.borrow();
*super::do_mir_borrowck(tcx, input_body, promoted, Some(options)).1.unwrap()
*super::do_mir_borrowck(tcx, def, Some(options)).1.unwrap()
}
48 changes: 9 additions & 39 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ use rustc_infer::infer::{
};
use rustc_middle::mir::*;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode, fold_regions};
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::impls::{
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
@@ -103,11 +103,8 @@ pub fn provide(providers: &mut Providers) {
}

fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def));

let (input_body, _0) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();

if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
debug!("Skipping borrowck because of injected body or tainted body");
// Let's make up a borrowck result! Fun times!
@@ -120,7 +117,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
return tcx.arena.alloc(result);
}

let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
let borrowck_result = do_mir_borrowck(tcx, def, None).0;
debug!("mir_borrowck done");

tcx.arena.alloc(borrowck_result)
@@ -131,15 +128,16 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
/// Use `consumer_options: None` for the default behavior of returning
/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
/// to the given [`ConsumerOptions`].
#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
#[instrument(skip(tcx), level = "debug")]
fn do_mir_borrowck<'tcx>(
tcx: TyCtxt<'tcx>,
input_body: &Body<'tcx>,
input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
def: LocalDefId,
consumer_options: Option<ConsumerOptions>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
let def = input_body.source.def_id().expect_local();
let infcx = BorrowckInferCtxt::new(tcx, def);
let (input_body, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();
let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
if let Some(e) = input_body.tainted_by_errors {
infcx.set_tainted_by_errors(e);
}
@@ -173,12 +171,6 @@ fn do_mir_borrowck<'tcx>(
let free_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted);
let body = &body_owned; // no further changes

// FIXME(-Znext-solver): A bit dubious that we're only registering
// predefined opaques in the typeck root.
if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
infcx.register_predefined_opaques_for_next_solver(def);
}

let location_table = PoloniusLocationTable::new(body);

let move_data = MoveData::gather_moves(body, tcx, |_| true);
@@ -433,7 +425,7 @@ pub(crate) struct BorrowckInferCtxt<'tcx> {

impl<'tcx> BorrowckInferCtxt<'tcx> {
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, def_id));
let infcx = tcx.infer_ctxt().build(TypingMode::borrowck(tcx, def_id));
let param_env = tcx.param_env(def_id);
BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
}
@@ -480,28 +472,6 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {

next_region
}

/// With the new solver we prepopulate the opaque type storage during
/// MIR borrowck with the hidden types from HIR typeck. This is necessary
/// to avoid ambiguities as earlier goals can rely on the hidden type
/// of an opaque which is only constrained by a later goal.
fn register_predefined_opaques_for_next_solver(&self, def_id: LocalDefId) {
let tcx = self.tcx;
// OK to use the identity arguments for each opaque type key, since
// we remap opaques from HIR typeck back to their definition params.
for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) {
// HIR typeck did not infer the regions of the opaque, so we instantiate
// them with fresh inference variables.
let (key, hidden_ty) = fold_regions(tcx, data, |_, _| {
self.next_nll_region_var_in_universe(
NllRegionVariableOrigin::Existential { from_forall: false },
ty::UniverseIndex::ROOT,
)
});

self.inject_new_hidden_type_unchecked(key, hidden_ty);
}
}
}

impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
169 changes: 5 additions & 164 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::OpaqueTyOrigin;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
use rustc_macros::extension;
use rustc_middle::ty::{
self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
TypeVisitableExt, TypingMode, fold_regions,
self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions,
};
use rustc_span::Span;
use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
use tracing::{debug, instrument};

use super::RegionInferenceContext;
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam};
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::universal_regions::RegionClassification;

impl<'tcx> RegionInferenceContext<'tcx> {
@@ -273,7 +267,7 @@ impl<'tcx> InferCtxt<'tcx> {
}

if let Err(guar) =
check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span)
check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span, false)
{
return Ty::new_error(self.tcx, guar);
}
@@ -289,156 +283,3 @@ impl<'tcx> InferCtxt<'tcx> {
definition_ty
}
}

/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
///
/// [rustc-dev-guide chapter]:
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
fn check_opaque_type_parameter_valid<'tcx>(
infcx: &InferCtxt<'tcx>,
opaque_type_key: OpaqueTypeKey<'tcx>,
span: Span,
) -> Result<(), ErrorGuaranteed> {
let tcx = infcx.tcx;
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();

for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
GenericArgKind::Lifetime(lt) => {
matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
|| (lt.is_static() && opaque_env.param_equal_static(i))
}
GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
};

if arg_is_param {
// Register if the same lifetime appears multiple times in the generic args.
// There is an exception when the opaque type *requires* the lifetimes to be equal.
// See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
let seen_where = seen_params.entry(arg).or_default();
if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
seen_where.push(i);
}
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = opaque_generics.param_at(i, tcx);
let kind = opaque_param.kind.descr();

opaque_env.param_is_error(i)?;

return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
ty: arg,
kind,
span,
param_span: tcx.def_span(opaque_param.def_id),
}));
}
}

for (_, indices) in seen_params {
if indices.len() > 1 {
let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
let spans: Vec<_> = indices
.into_iter()
.map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
.collect();
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
return Err(infcx
.dcx()
.struct_span_err(span, "non-defining opaque type use in defining scope")
.with_span_note(spans, format!("{descr} used multiple times"))
.emit());
}
}

Ok(())
}

/// Computes if an opaque type requires a lifetime parameter to be equal to
/// another one or to the `'static` lifetime.
/// These requirements are derived from the explicit and implied bounds.
struct LazyOpaqueTyEnv<'tcx> {
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,

/// Equal parameters will have the same name. Computed Lazily.
/// Example:
/// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
/// Identity args: `['a, 'b, 'c]`
/// Canonical args: `['static, 'b, 'b]`
canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
}

impl<'tcx> LazyOpaqueTyEnv<'tcx> {
fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
}

fn param_equal_static(&self, param_index: usize) -> bool {
self.get_canonical_args()[param_index].expect_region().is_static()
}

fn params_equal(&self, param1: usize, param2: usize) -> bool {
let canonical_args = self.get_canonical_args();
canonical_args[param1] == canonical_args[param2]
}

fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> {
self.get_canonical_args()[param_index].error_reported()
}

fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
if let Some(&canonical_args) = self.canonical_args.get() {
return canonical_args;
}

let &Self { tcx, def_id, .. } = self;
let origin = tcx.local_opaque_ty_origin(def_id);
let parent = match origin {
OpaqueTyOrigin::FnReturn { parent, .. }
| OpaqueTyOrigin::AsyncFn { parent, .. }
| OpaqueTyOrigin::TyAlias { parent, .. } => parent,
};
let param_env = tcx.param_env(parent);
let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
tcx,
def_id.to_def_id(),
|param, _| {
tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
},
);

// FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're
// in a body here.
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new(&infcx);

let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
Default::default()
});
let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);

let mut seen = vec![tcx.lifetimes.re_static];
let canonical_args = fold_regions(tcx, args, |r1, _| {
if r1.is_error() {
r1
} else if let Some(&r2) = seen.iter().find(|&&r2| {
let free_regions = outlives_env.free_region_map();
free_regions.sub_free_regions(tcx, r1, r2)
&& free_regions.sub_free_regions(tcx, r2, r1)
}) {
r2
} else {
seen.push(r1);
r1
}
});
self.canonical_args.set(canonical_args).unwrap();
canonical_args
}
}
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.