Skip to content

Commit

Permalink
Rollup merge of #120390 - matthewjasper:inline-constant-pat-mir, r=da…
Browse files Browse the repository at this point in the history
…vidtwco

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.
Also enables some inline constant pattern tests that were fixed by the THIR unsafeck stabilization.

cc #76001
  • Loading branch information
Dylan-DPC authored Jan 29, 2024
2 parents d04bede + 44824e0 commit 549eeb0
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 18 deletions.
64 changes: 56 additions & 8 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1099,10 +1099,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn check_user_type_annotations(&mut self) {
debug!(?self.user_type_annotations);
let tcx = self.tcx();
for user_annotation in self.user_type_annotations {
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
self.ascribe_user_type(inferred_ty, annotation, span);
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);
}
}
}

Expand Down Expand Up @@ -1195,6 +1202,36 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
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> {
self.infcx.tcx
}
Expand Down Expand Up @@ -1851,7 +1888,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let def_id = uv.def;
if tcx.def_kind(def_id) == DefKind::InlineConst {
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(
def_id.to_def_id(),
predicates,
Expand Down Expand Up @@ -2654,9 +2696,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// desugaring. A closure gets desugared to a struct, and
// these extra requirements are basically like where
// clauses on the struct.
AggregateKind::Closure(def_id, args) | AggregateKind::Coroutine(def_id, args) => {
(def_id, self.prove_closure_bounds(tcx, def_id.expect_local(), args, location))
}
AggregateKind::Closure(def_id, args) | AggregateKind::Coroutine(def_id, args) => (
def_id,
self.prove_closure_bounds(
tcx,
def_id.expect_local(),
args,
location.to_locations(),
),
),

AggregateKind::Array(_) | AggregateKind::Tuple => {
(CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty())
Expand All @@ -2675,7 +2723,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
args: GenericArgsRef<'tcx>,
location: Location,
locations: Locations,
) -> ty::InstantiatedPredicates<'tcx> {
if let Some(closure_requirements) = &tcx.mir_borrowck(def_id).closure_requirements {
constraint_conversion::ConstraintConversion::new(
Expand All @@ -2684,7 +2732,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
location.to_locations(),
locations,
DUMMY_SP, // irrelevant; will be overridden.
ConstraintCategory::Boring, // same as above.
self.borrowck_context.constraints,
Expand All @@ -2710,7 +2758,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
if let Err(_) = self.eq_args(
typeck_root_args,
parent_args,
location.to_locations(),
locations,
ConstraintCategory::BoringNoLocation,
) {
span_mirbug!(
Expand Down
37 changes: 35 additions & 2 deletions compiler/rustc_mir_build/src/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
use crate::build::Builder;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::thir::{self, *};
use rustc_middle::ty;

use std::mem;

Expand Down Expand Up @@ -149,7 +151,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ref subpattern,
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) {
candidate.ascriptions.push(Ascription {
annotation: annotation.clone(),
Expand Down Expand Up @@ -205,7 +207,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
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));

Ok(())
Expand Down
25 changes: 20 additions & 5 deletions tests/ui/inline-const/const-match-pat-lifetime-err.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore-test (This is currently broken)

#![allow(incomplete_features)]
#![feature(const_mut_refs)]
#![feature(inline_const_pat)]
Expand All @@ -9,6 +7,9 @@ use std::marker::PhantomData;
#[derive(PartialEq, Eq)]
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> {
pub const fn new(r: &'a T) -> Self {
InvariantRef(r, PhantomData)
Expand All @@ -19,16 +20,30 @@ impl<'a> InvariantRef<'a, ()> {
pub const NEW: Self = InvariantRef::new(&());
}

impl<'a> CovariantRef<'a, ()> {
pub const NEW: Self = CovariantRef(&());
}

fn match_invariant_ref<'a>() {
let y = ();
match InvariantRef::new(&y) {
//~^ ERROR `y` does not live long enough [E0597]
// FIXME(nbdd0121): This should give the same error as `InvariantRef::<'a>::NEW` (without
// const block)
//~^ ERROR `y` does not live long enough [E0597]
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() {
match_invariant_ref();
match_covariant_ref();
}
28 changes: 28 additions & 0 deletions tests/ui/inline-const/const-match-pat-lifetime-err.stderr
Original file line number Diff line number Diff line change
@@ -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`.
2 changes: 0 additions & 2 deletions tests/ui/inline-const/pat-unsafe-err.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore-test This is currently broken

#![allow(incomplete_features)]
#![feature(inline_const_pat)]

Expand Down
19 changes: 19 additions & 0 deletions tests/ui/inline-const/pat-unsafe-err.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0133]: call to unsafe function `require_unsafe` is unsafe and requires unsafe function or block
--> $DIR/pat-unsafe-err.rs:11:13
|
LL | require_unsafe();
| ^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior

error[E0133]: call to unsafe function `require_unsafe` is unsafe and requires unsafe function or block
--> $DIR/pat-unsafe-err.rs:18:13
|
LL | require_unsafe()
| ^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0133`.
1 change: 0 additions & 1 deletion tests/ui/inline-const/pat-unsafe.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// check-pass
// ignore-test This is currently broken

#![allow(incomplete_features)]
#![warn(unused_unsafe)]
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/inline-const/pat-unsafe.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
warning: unnecessary `unsafe` block
--> $DIR/pat-unsafe.rs:16:17
|
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
|
note: the lint level is defined here
--> $DIR/pat-unsafe.rs:4:9
|
LL | #![warn(unused_unsafe)]
| ^^^^^^^^^^^^^

warning: unnecessary `unsafe` block
--> $DIR/pat-unsafe.rs:23:17
|
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block

warning: 2 warnings emitted

0 comments on commit 549eeb0

Please sign in to comment.