Borrow check inline const patterns
Add type annotations to MIR so that borrowck can pass constraints from inline constants in patterns to the containing function.
This commit is contained in:
parent
e35a56d96f
commit
83fa46fe5b
4 changed files with 139 additions and 15 deletions
|
@ -1099,12 +1099,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
#[instrument(skip(self), level = "debug")]
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn check_user_type_annotations(&mut self) {
|
fn check_user_type_annotations(&mut self) {
|
||||||
debug!(?self.user_type_annotations);
|
debug!(?self.user_type_annotations);
|
||||||
|
let tcx = self.tcx();
|
||||||
for user_annotation in self.user_type_annotations {
|
for user_annotation in self.user_type_annotations {
|
||||||
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
|
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
|
||||||
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
|
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
|
||||||
|
if let ty::UserType::TypeOf(def, args) = annotation
|
||||||
|
&& let DefKind::InlineConst = tcx.def_kind(def)
|
||||||
|
{
|
||||||
|
self.check_inline_const(inferred_ty, def.expect_local(), args, span);
|
||||||
|
} else {
|
||||||
self.ascribe_user_type(inferred_ty, annotation, span);
|
self.ascribe_user_type(inferred_ty, annotation, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, data), level = "debug")]
|
#[instrument(skip(self, data), level = "debug")]
|
||||||
fn push_region_constraints(
|
fn push_region_constraints(
|
||||||
|
@ -1195,6 +1202,36 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_inline_const(
|
||||||
|
&mut self,
|
||||||
|
inferred_ty: Ty<'tcx>,
|
||||||
|
def_id: LocalDefId,
|
||||||
|
args: UserArgs<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
assert!(args.user_self_ty.is_none());
|
||||||
|
let tcx = self.tcx();
|
||||||
|
let const_ty = tcx.type_of(def_id).instantiate(tcx, args.args);
|
||||||
|
if let Err(terr) =
|
||||||
|
self.eq_types(const_ty, inferred_ty, Locations::All(span), ConstraintCategory::Boring)
|
||||||
|
{
|
||||||
|
span_bug!(
|
||||||
|
span,
|
||||||
|
"bad inline const pattern: ({:?} = {:?}) {:?}",
|
||||||
|
const_ty,
|
||||||
|
inferred_ty,
|
||||||
|
terr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let args = self.infcx.resolve_vars_if_possible(args.args);
|
||||||
|
let predicates = self.prove_closure_bounds(tcx, def_id, args, Locations::All(span));
|
||||||
|
self.normalize_and_prove_instantiated_predicates(
|
||||||
|
def_id.to_def_id(),
|
||||||
|
predicates,
|
||||||
|
Locations::All(span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||||
self.infcx.tcx
|
self.infcx.tcx
|
||||||
}
|
}
|
||||||
|
@ -1851,7 +1888,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
let def_id = uv.def;
|
let def_id = uv.def;
|
||||||
if tcx.def_kind(def_id) == DefKind::InlineConst {
|
if tcx.def_kind(def_id) == DefKind::InlineConst {
|
||||||
let def_id = def_id.expect_local();
|
let def_id = def_id.expect_local();
|
||||||
let predicates = self.prove_closure_bounds(tcx, def_id, uv.args, location);
|
let predicates = self.prove_closure_bounds(
|
||||||
|
tcx,
|
||||||
|
def_id,
|
||||||
|
uv.args,
|
||||||
|
location.to_locations(),
|
||||||
|
);
|
||||||
self.normalize_and_prove_instantiated_predicates(
|
self.normalize_and_prove_instantiated_predicates(
|
||||||
def_id.to_def_id(),
|
def_id.to_def_id(),
|
||||||
predicates,
|
predicates,
|
||||||
|
@ -2654,9 +2696,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
// desugaring. A closure gets desugared to a struct, and
|
// desugaring. A closure gets desugared to a struct, and
|
||||||
// these extra requirements are basically like where
|
// these extra requirements are basically like where
|
||||||
// clauses on the struct.
|
// clauses on the struct.
|
||||||
AggregateKind::Closure(def_id, args) | AggregateKind::Coroutine(def_id, args) => {
|
AggregateKind::Closure(def_id, args) | AggregateKind::Coroutine(def_id, args) => (
|
||||||
(def_id, self.prove_closure_bounds(tcx, def_id.expect_local(), args, location))
|
def_id,
|
||||||
}
|
self.prove_closure_bounds(
|
||||||
|
tcx,
|
||||||
|
def_id.expect_local(),
|
||||||
|
args,
|
||||||
|
location.to_locations(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
AggregateKind::Array(_) | AggregateKind::Tuple => {
|
AggregateKind::Array(_) | AggregateKind::Tuple => {
|
||||||
(CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty())
|
(CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty())
|
||||||
|
@ -2675,7 +2723,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
def_id: LocalDefId,
|
def_id: LocalDefId,
|
||||||
args: GenericArgsRef<'tcx>,
|
args: GenericArgsRef<'tcx>,
|
||||||
location: Location,
|
locations: Locations,
|
||||||
) -> ty::InstantiatedPredicates<'tcx> {
|
) -> ty::InstantiatedPredicates<'tcx> {
|
||||||
if let Some(closure_requirements) = &tcx.mir_borrowck(def_id).closure_requirements {
|
if let Some(closure_requirements) = &tcx.mir_borrowck(def_id).closure_requirements {
|
||||||
constraint_conversion::ConstraintConversion::new(
|
constraint_conversion::ConstraintConversion::new(
|
||||||
|
@ -2684,7 +2732,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
self.region_bound_pairs,
|
self.region_bound_pairs,
|
||||||
self.implicit_region_bound,
|
self.implicit_region_bound,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
location.to_locations(),
|
locations,
|
||||||
DUMMY_SP, // irrelevant; will be overridden.
|
DUMMY_SP, // irrelevant; will be overridden.
|
||||||
ConstraintCategory::Boring, // same as above.
|
ConstraintCategory::Boring, // same as above.
|
||||||
self.borrowck_context.constraints,
|
self.borrowck_context.constraints,
|
||||||
|
@ -2710,7 +2758,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
if let Err(_) = self.eq_args(
|
if let Err(_) = self.eq_args(
|
||||||
typeck_root_args,
|
typeck_root_args,
|
||||||
parent_args,
|
parent_args,
|
||||||
location.to_locations(),
|
locations,
|
||||||
ConstraintCategory::BoringNoLocation,
|
ConstraintCategory::BoringNoLocation,
|
||||||
) {
|
) {
|
||||||
span_mirbug!(
|
span_mirbug!(
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
use crate::build::expr::as_place::PlaceBuilder;
|
use crate::build::expr::as_place::PlaceBuilder;
|
||||||
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
|
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
|
||||||
use crate::build::Builder;
|
use crate::build::Builder;
|
||||||
|
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use rustc_middle::thir::{self, *};
|
use rustc_middle::thir::{self, *};
|
||||||
|
use rustc_middle::ty;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -149,7 +151,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
ref subpattern,
|
ref subpattern,
|
||||||
ascription: thir::Ascription { ref annotation, variance },
|
ascription: thir::Ascription { ref annotation, variance },
|
||||||
} => {
|
} => {
|
||||||
// Apply the type ascription to the value at `match_pair.place`, which is the
|
// Apply the type ascription to the value at `match_pair.place`
|
||||||
if let Some(source) = match_pair.place.try_to_place(self) {
|
if let Some(source) = match_pair.place.try_to_place(self) {
|
||||||
candidate.ascriptions.push(Ascription {
|
candidate.ascriptions.push(Ascription {
|
||||||
annotation: annotation.clone(),
|
annotation: annotation.clone(),
|
||||||
|
@ -205,7 +207,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
Err(match_pair)
|
Err(match_pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
PatKind::InlineConstant { subpattern: ref pattern, def: _ } => {
|
PatKind::InlineConstant { subpattern: ref pattern, def } => {
|
||||||
|
// Apply a type ascription for the inline constant to the value at `match_pair.place`
|
||||||
|
if let Some(source) = match_pair.place.try_to_place(self) {
|
||||||
|
let span = match_pair.pattern.span;
|
||||||
|
let parent_id = self.tcx.typeck_root_def_id(self.def_id.to_def_id());
|
||||||
|
let args = ty::InlineConstArgs::new(
|
||||||
|
self.tcx,
|
||||||
|
ty::InlineConstArgsParts {
|
||||||
|
parent_args: ty::GenericArgs::identity_for_item(self.tcx, parent_id),
|
||||||
|
ty: self.infcx.next_ty_var(TypeVariableOrigin {
|
||||||
|
kind: TypeVariableOriginKind::MiscVariable,
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.args;
|
||||||
|
let user_ty =
|
||||||
|
self.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
|
||||||
|
def.to_def_id(),
|
||||||
|
ty::UserArgs { args, user_self_ty: None },
|
||||||
|
));
|
||||||
|
let annotation = ty::CanonicalUserTypeAnnotation {
|
||||||
|
inferred_ty: pattern.ty,
|
||||||
|
span,
|
||||||
|
user_ty: Box::new(user_ty),
|
||||||
|
};
|
||||||
|
candidate.ascriptions.push(Ascription {
|
||||||
|
annotation,
|
||||||
|
source,
|
||||||
|
variance: ty::Contravariant,
|
||||||
|
});
|
||||||
|
}
|
||||||
candidate.match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
|
candidate.match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// ignore-test (This is currently broken)
|
|
||||||
|
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(const_mut_refs)]
|
#![feature(const_mut_refs)]
|
||||||
#![feature(inline_const_pat)]
|
#![feature(inline_const_pat)]
|
||||||
|
@ -9,6 +7,9 @@ use std::marker::PhantomData;
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
|
pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub struct CovariantRef<'a, T: ?Sized>(&'a T);
|
||||||
|
|
||||||
impl<'a, T: ?Sized> InvariantRef<'a, T> {
|
impl<'a, T: ?Sized> InvariantRef<'a, T> {
|
||||||
pub const fn new(r: &'a T) -> Self {
|
pub const fn new(r: &'a T) -> Self {
|
||||||
InvariantRef(r, PhantomData)
|
InvariantRef(r, PhantomData)
|
||||||
|
@ -19,16 +20,30 @@ impl<'a> InvariantRef<'a, ()> {
|
||||||
pub const NEW: Self = InvariantRef::new(&());
|
pub const NEW: Self = InvariantRef::new(&());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> CovariantRef<'a, ()> {
|
||||||
|
pub const NEW: Self = CovariantRef(&());
|
||||||
|
}
|
||||||
|
|
||||||
fn match_invariant_ref<'a>() {
|
fn match_invariant_ref<'a>() {
|
||||||
let y = ();
|
let y = ();
|
||||||
match InvariantRef::new(&y) {
|
match InvariantRef::new(&y) {
|
||||||
//~^ ERROR `y` does not live long enough [E0597]
|
//~^ ERROR `y` does not live long enough [E0597]
|
||||||
// FIXME(nbdd0121): This should give the same error as `InvariantRef::<'a>::NEW` (without
|
|
||||||
// const block)
|
|
||||||
const { InvariantRef::<'a>::NEW } => (),
|
const { InvariantRef::<'a>::NEW } => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn match_covariant_ref<'a>() {
|
||||||
|
// Unclear if we should error here (should we be able to subtype the type of
|
||||||
|
// `y.0`), but using the associated const directly in the pattern also
|
||||||
|
// errors.
|
||||||
|
let y: (CovariantRef<'static, _>,) = (CovariantRef(&()),);
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
|
match y.0 {
|
||||||
|
const { CovariantRef::<'a>::NEW } => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match_invariant_ref();
|
match_invariant_ref();
|
||||||
|
match_covariant_ref();
|
||||||
}
|
}
|
||||||
|
|
28
tests/ui/inline-const/const-match-pat-lifetime-err.stderr
Normal file
28
tests/ui/inline-const/const-match-pat-lifetime-err.stderr
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
error[E0597]: `y` does not live long enough
|
||||||
|
--> $DIR/const-match-pat-lifetime-err.rs:29:29
|
||||||
|
|
|
||||||
|
LL | fn match_invariant_ref<'a>() {
|
||||||
|
| -- lifetime `'a` defined here
|
||||||
|
LL | let y = ();
|
||||||
|
| - binding `y` declared here
|
||||||
|
LL | match InvariantRef::new(&y) {
|
||||||
|
| ^^ borrowed value does not live long enough
|
||||||
|
LL |
|
||||||
|
LL | const { InvariantRef::<'a>::NEW } => (),
|
||||||
|
| --------------------------------- type annotation requires that `y` is borrowed for `'a`
|
||||||
|
LL | }
|
||||||
|
LL | }
|
||||||
|
| - `y` dropped here while still borrowed
|
||||||
|
|
||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/const-match-pat-lifetime-err.rs:39:12
|
||||||
|
|
|
||||||
|
LL | fn match_covariant_ref<'a>() {
|
||||||
|
| -- lifetime `'a` defined here
|
||||||
|
...
|
||||||
|
LL | let y: (CovariantRef<'static, _>,) = (CovariantRef(&()),);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0597`.
|
Loading…
Add table
Add a link
Reference in a new issue