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

Borrow check inline const patterns #120390

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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