Auto merge of #132275 - compiler-errors:deref-effects, r=fee1-dead

Register `~const` preds for `Deref` adjustments in HIR typeck

This doesn't *do* anything yet, since `Deref` and `DerefMut` aren't constified, and we explicitly don't error on calling non-const trait methods in HIR yet -- presumably that will wait until std is re-constified. But I'm confident this logic is correct, and this (afaict?) is the only major hole left in enforcing `~const` in HIR typeck.

r? fee1-dead
This commit is contained in:
bors 2024-11-04 12:12:55 +00:00
commit 432972cae6
9 changed files with 139 additions and 140 deletions

View File

@ -254,10 +254,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
for a in &adj { for a in &adj {
if let Adjust::NeverToAny = a.kind { match a.kind {
if a.target.is_ty_var() { Adjust::NeverToAny => {
self.diverging_type_vars.borrow_mut().insert(a.target); if a.target.is_ty_var() {
debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target); self.diverging_type_vars.borrow_mut().insert(a.target);
debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target);
}
}
Adjust::Deref(Some(overloaded_deref)) => {
self.enforce_context_effects(
expr.span,
overloaded_deref.method_call(self.tcx),
self.tcx.mk_args(&[a.target.into()]),
);
}
Adjust::Deref(None) => {
// FIXME(effects): We *could* enforce `&T: ~const Deref` here.
}
Adjust::Pointer(_pointer_coercion) => {
// FIXME(effects): We should probably enforce these.
}
Adjust::ReborrowPin(_mutability) => {
// FIXME(effects): We could enforce these; they correspond to
// `&mut T: DerefMut` tho, so it's kinda moot.
}
Adjust::Borrow(_) => {
// No effects to enforce here.
} }
} }
} }

View File

@ -296,6 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
); );
}; };
*deref = OverloadedDeref { mutbl, span: deref.span }; *deref = OverloadedDeref { mutbl, span: deref.span };
self.enforce_context_effects(expr.span, method.def_id, method.args);
// If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
// This helps avoid accidental drops. // This helps avoid accidental drops.
if inside_union if inside_union

View File

@ -1,5 +1,6 @@
use rustc_abi::FieldIdx; use rustc_abi::FieldIdx;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_span::Span; use rustc_span::Span;
@ -123,19 +124,18 @@ pub struct OverloadedDeref {
} }
impl OverloadedDeref { impl OverloadedDeref {
/// Get the zst function item type for this method call. /// Get the [`DefId`] of the method call for the given `Deref`/`DerefMut` trait
pub fn method_call<'tcx>(&self, tcx: TyCtxt<'tcx>, source: Ty<'tcx>) -> Ty<'tcx> { /// for this overloaded deref's mutability.
pub fn method_call<'tcx>(&self, tcx: TyCtxt<'tcx>) -> DefId {
let trait_def_id = match self.mutbl { let trait_def_id = match self.mutbl {
hir::Mutability::Not => tcx.require_lang_item(LangItem::Deref, None), hir::Mutability::Not => tcx.require_lang_item(LangItem::Deref, None),
hir::Mutability::Mut => tcx.require_lang_item(LangItem::DerefMut, None), hir::Mutability::Mut => tcx.require_lang_item(LangItem::DerefMut, None),
}; };
let method_def_id = tcx tcx.associated_items(trait_def_id)
.associated_items(trait_def_id)
.in_definition_order() .in_definition_order()
.find(|m| m.kind == ty::AssocKind::Fn) .find(|m| m.kind == ty::AssocKind::Fn)
.unwrap() .unwrap()
.def_id; .def_id
Ty::new_fn_def(tcx, method_def_id, [source])
} }
} }

View File

@ -136,7 +136,9 @@ impl<'tcx> Cx<'tcx> {
Adjust::Deref(Some(deref)) => { Adjust::Deref(Some(deref)) => {
// We don't need to do call adjust_span here since // We don't need to do call adjust_span here since
// deref coercions always start with a built-in deref. // deref coercions always start with a built-in deref.
let call = deref.method_call(self.tcx(), expr.ty); let call_def_id = deref.method_call(self.tcx());
let overloaded_callee =
Ty::new_fn_def(self.tcx(), call_def_id, self.tcx().mk_args(&[expr.ty.into()]));
expr = Expr { expr = Expr {
temp_lifetime, temp_lifetime,
@ -150,7 +152,13 @@ impl<'tcx> Cx<'tcx> {
let expr = Box::new([self.thir.exprs.push(expr)]); let expr = Box::new([self.thir.exprs.push(expr)]);
self.overloaded_place(hir_expr, adjustment.target, Some(call), expr, deref.span) self.overloaded_place(
hir_expr,
adjustment.target,
Some(overloaded_callee),
expr,
deref.span,
)
} }
Adjust::Borrow(AutoBorrow::Ref(m)) => ExprKind::Borrow { Adjust::Borrow(AutoBorrow::Ref(m)) => ExprKind::Borrow {
borrow_kind: m.to_borrow_kind(), borrow_kind: m.to_borrow_kind(),

View File

@ -1,12 +1,7 @@
//@ known-bug: #110395 //@ compile-flags: -Znext-solver -Cpanic=abort
//@ failure-status: 101 //@ no-prefer-dynamic
//@ normalize-stderr-test: ".*note: .*\n\n" -> ""
//@ normalize-stderr-test: "thread 'rustc' panicked.*:\n.*\n" -> ""
//@ rustc-env:RUST_BACKTRACE=0
// FIXME(const_trait_impl) check-pass
//@ compile-flags: -Znext-solver
#![crate_type = "lib"] #![crate_type = "rlib"]
#![feature( #![feature(
no_core, no_core,
lang_items, lang_items,
@ -23,13 +18,17 @@
#![no_core] #![no_core]
#[lang = "sized"] #[lang = "sized"]
trait Sized {} pub trait Sized {}
#[lang = "copy"] #[lang = "copy"]
trait Copy {} pub trait Copy {}
impl Copy for bool {}
impl Copy for u8 {}
impl<T: ?Sized> Copy for &T {}
#[lang = "add"] #[lang = "add"]
#[const_trait] #[const_trait]
trait Add<Rhs = Self> { pub trait Add<Rhs = Self> {
type Output; type Output;
fn add(self, rhs: Rhs) -> Self::Output; fn add(self, rhs: Rhs) -> Self::Output;
@ -50,10 +49,9 @@ const fn bar() {
let x = 42_i32 + 43_i32; let x = 42_i32 + 43_i32;
} }
#[lang = "Try"] #[lang = "Try"]
#[const_trait] #[const_trait]
trait Try: FromResidual<Self::Residual> { pub trait Try: FromResidual<Self::Residual> {
type Output; type Output;
type Residual; type Residual;
@ -64,9 +62,8 @@ trait Try: FromResidual<Self::Residual> {
fn branch(self) -> ControlFlow<Self::Residual, Self::Output>; fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
} }
// FIXME #[const_trait]
// #[const_trait] pub trait FromResidual<R = <Self as Try>::Residual> {
trait FromResidual<R = <Self as /* FIXME: ~const */ Try>::Residual> {
#[lang = "from_residual"] #[lang = "from_residual"]
fn from_residual(residual: R) -> Self; fn from_residual(residual: R) -> Self;
} }
@ -81,71 +78,32 @@ enum ControlFlow<B, C = ()> {
#[const_trait] #[const_trait]
#[lang = "fn"] #[lang = "fn"]
#[rustc_paren_sugar] #[rustc_paren_sugar]
trait Fn<Args: Tuple>: ~const FnMut<Args> { pub trait Fn<Args: Tuple>: ~const FnMut<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output; extern "rust-call" fn call(&self, args: Args) -> Self::Output;
} }
#[const_trait] #[const_trait]
#[lang = "fn_mut"] #[lang = "fn_mut"]
#[rustc_paren_sugar] #[rustc_paren_sugar]
trait FnMut<Args: Tuple>: ~const FnOnce<Args> { pub trait FnMut<Args: Tuple>: ~const FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
} }
#[const_trait] #[const_trait]
#[lang = "fn_once"] #[lang = "fn_once"]
#[rustc_paren_sugar] #[rustc_paren_sugar]
trait FnOnce<Args: Tuple> { pub trait FnOnce<Args: Tuple> {
#[lang = "fn_once_output"] #[lang = "fn_once_output"]
type Output; type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
} }
struct ConstFnMutClosure<CapturedData, Function> {
data: CapturedData,
func: Function,
}
#[lang = "tuple_trait"] #[lang = "tuple_trait"]
trait Tuple {} pub trait Tuple {}
macro_rules! impl_fn_mut_tuple {
($($var:ident)*) => {
impl<'a, $($var,)* ClosureArguments: Tuple, Function, ClosureReturnValue> const
FnOnce<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
where
Function: ~const Fn(($(&mut $var),*), ClosureArguments) -> ClosureReturnValue,
Function: ~const Destruct,
{
type Output = ClosureReturnValue;
extern "rust-call" fn call_once(mut self, args: ClosureArguments) -> Self::Output {
self.call_mut(args)
}
}
impl<'a, $($var,)* ClosureArguments: Tuple, Function, ClosureReturnValue> const
FnMut<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
where
Function: ~const Fn(($(&mut $var),*), ClosureArguments)-> ClosureReturnValue,
Function: ~const Destruct,
{
extern "rust-call" fn call_mut(&mut self, args: ClosureArguments) -> Self::Output {
#[allow(non_snake_case)]
let ($($var),*) = &mut self.data;
(self.func)(($($var),*), args)
}
}
};
}
//impl_fn_mut_tuple!(A);
//impl_fn_mut_tuple!(A B);
//impl_fn_mut_tuple!(A B C);
//impl_fn_mut_tuple!(A B C D);
//impl_fn_mut_tuple!(A B C D E);
#[lang = "legacy_receiver"] #[lang = "legacy_receiver"]
trait LegacyReceiver {} pub trait LegacyReceiver {}
impl<T: ?Sized> LegacyReceiver for &T {} impl<T: ?Sized> LegacyReceiver for &T {}
@ -153,30 +111,26 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
#[lang = "destruct"] #[lang = "destruct"]
#[const_trait] #[const_trait]
trait Destruct {} pub trait Destruct {}
#[lang = "freeze"] #[lang = "freeze"]
unsafe auto trait Freeze {} pub unsafe auto trait Freeze {}
#[lang = "drop"] #[lang = "drop"]
#[const_trait] #[const_trait]
trait Drop { pub trait Drop {
fn drop(&mut self); fn drop(&mut self);
} }
/*
#[const_trait] #[const_trait]
trait Residual<O> { pub trait Residual<O> {
type TryType: ~const Try<Output = O, Residual = Self> + Try<Output = O, Residual = Self>; type TryType: ~const Try<Output = O, Residual = Self> + Try<Output = O, Residual = Self>;
} }
*/
const fn size_of<T>() -> usize { const fn size_of<T>() -> usize {
42 42
} }
impl Copy for u8 {}
impl usize { impl usize {
#[rustc_allow_incoherent_impl] #[rustc_allow_incoherent_impl]
const fn repeat_u8(x: u8) -> usize { const fn repeat_u8(x: u8) -> usize {
@ -197,15 +151,14 @@ fn panic_fmt() {}
#[lang = "index"] #[lang = "index"]
#[const_trait] #[const_trait]
trait Index<Idx: ?Sized> { pub trait Index<Idx: ?Sized> {
type Output: ?Sized; type Output: ?Sized;
fn index(&self, index: Idx) -> &Self::Output; fn index(&self, index: Idx) -> &Self::Output;
} }
#[const_trait] #[const_trait]
unsafe trait SliceIndex<T: ?Sized> { pub unsafe trait SliceIndex<T: ?Sized> {
type Output: ?Sized; type Output: ?Sized;
fn index(self, slice: &T) -> &Self::Output; fn index(self, slice: &T) -> &Self::Output;
} }
@ -221,7 +174,7 @@ where
index.index(self) index.index(self)
} }
} }
/* FIXME
impl<T, I, const N: usize> const Index<I> for [T; N] impl<T, I, const N: usize> const Index<I> for [T; N]
where where
[T]: ~const Index<I>, [T]: ~const Index<I>,
@ -229,35 +182,29 @@ where
type Output = <[T] as Index<I>>::Output; type Output = <[T] as Index<I>>::Output;
#[inline] #[inline]
// FIXME: make `Self::Output` act like `<Self as ~const Index<I>>::Output`
fn index(&self, index: I) -> &<[T] as Index<I>>::Output { fn index(&self, index: I) -> &<[T] as Index<I>>::Output {
Index::index(self as &[T], index) Index::index(self as &[T], index)
} }
} }
*/
#[lang = "unsize"] #[lang = "unsize"]
trait Unsize<T: ?Sized> { pub trait Unsize<T: ?Sized> {}
}
#[lang = "coerce_unsized"] #[lang = "coerce_unsized"]
trait CoerceUnsized<T: ?Sized> { pub trait CoerceUnsized<T: ?Sized> {}
}
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
#[lang = "deref"] #[lang = "deref"]
// #[const_trait] FIXME #[const_trait]
trait Deref { pub trait Deref {
#[lang = "deref_target"] #[lang = "deref_target"]
type Target: ?Sized; type Target: ?Sized;
fn deref(&self) -> &Self::Target; fn deref(&self) -> &Self::Target;
} }
impl<T: ?Sized> const Deref for &T {
impl<T: ?Sized> /* const */ Deref for &T {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
@ -265,7 +212,7 @@ impl<T: ?Sized> /* const */ Deref for &T {
} }
} }
impl<T: ?Sized> /* const */ Deref for &mut T { impl<T: ?Sized> const Deref for &mut T {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
@ -298,7 +245,6 @@ impl<T> Option<T> {
use Option::*; use Option::*;
/*
const fn as_deref<T>(opt: &Option<T>) -> Option<&T::Target> const fn as_deref<T>(opt: &Option<T>) -> Option<&T::Target>
where where
T: ~const Deref, T: ~const Deref,
@ -308,15 +254,14 @@ where
Option::None => Option::None, Option::None => Option::None,
} }
} }
*/
#[const_trait] #[const_trait]
trait Into<T>: Sized { pub trait Into<T>: Sized {
fn into(self) -> T; fn into(self) -> T;
} }
#[const_trait] #[const_trait]
trait From<T>: Sized { pub trait From<T>: Sized {
fn from(value: T) -> Self; fn from(value: T) -> Self;
} }
@ -351,7 +296,7 @@ fn from_str(s: &str) -> Result<bool, ()> {
#[lang = "eq"] #[lang = "eq"]
#[const_trait] #[const_trait]
trait PartialEq<Rhs: ?Sized = Self> { pub trait PartialEq<Rhs: ?Sized = Self> {
fn eq(&self, other: &Rhs) -> bool; fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { fn ne(&self, other: &Rhs) -> bool {
!self.eq(other) !self.eq(other)
@ -373,10 +318,9 @@ impl PartialEq for str {
} }
} }
#[lang = "not"] #[lang = "not"]
#[const_trait] #[const_trait]
trait Not { pub trait Not {
type Output; type Output;
fn not(self) -> Self::Output; fn not(self) -> Self::Output;
} }
@ -388,9 +332,6 @@ impl const Not for bool {
} }
} }
impl Copy for bool {}
impl<'a> Copy for &'a str {}
#[lang = "pin"] #[lang = "pin"]
#[fundamental] #[fundamental]
#[repr(transparent)] #[repr(transparent)]
@ -411,23 +352,21 @@ impl<'a, T: ?Sized> Pin<&'a T> {
} }
} }
impl<P: Deref> Pin<P> { impl<P: Deref> Pin<P> {
/* const */ fn as_ref(&self) -> Pin<&P::Target> const fn as_ref(&self) -> Pin<&P::Target>
where where
P: /* ~const */ Deref, P: ~const Deref,
{ {
unsafe { Pin::new_unchecked(&*self.pointer) } unsafe { Pin::new_unchecked(&*self.pointer) }
} }
} }
impl<'a, T: ?Sized> Pin<&'a mut T> { impl<'a, T: ?Sized> Pin<&'a mut T> {
const unsafe fn get_unchecked_mut(self) -> &'a mut T { const unsafe fn get_unchecked_mut(self) -> &'a mut T {
self.pointer self.pointer
} }
} }
/* FIXME lol
impl<T> Option<T> { impl<T> Option<T> {
const fn as_pin_ref(self: Pin<&Self>) -> Option<Pin<&T>> { const fn as_pin_ref(self: Pin<&Self>) -> Option<Pin<&T>> {
match Pin::get_ref(self).as_ref() { match Pin::get_ref(self).as_ref() {
@ -445,16 +384,15 @@ impl<T> Option<T> {
} }
} }
} }
*/
impl<P: /* ~const */ Deref> /* const */ Deref for Pin<P> { impl<P: ~const Deref> const Deref for Pin<P> {
type Target = P::Target; type Target = P::Target;
fn deref(&self) -> &P::Target { fn deref(&self) -> &P::Target {
Pin::get_ref(Pin::as_ref(self)) Pin::get_ref(Pin::as_ref(self))
} }
} }
impl<T> /* const */ Deref for Option<T> { impl<T> const Deref for Option<T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
loop {} loop {}
@ -506,23 +444,22 @@ impl<T: ?Sized> Deref for Ref<'_, T> {
#[lang = "clone"] #[lang = "clone"]
#[rustc_trivial_field_reads] #[rustc_trivial_field_reads]
#[const_trait] // FIXME: #[const_trait]
trait Clone: Sized { pub trait Clone: Sized {
fn clone(&self) -> Self; fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) fn clone_from(&mut self, source: &Self)
where where
Self: ~const Destruct, // FIXME: Self: ~const Destruct,
{ {
*self = source.clone() *self = source.clone()
} }
} }
#[lang = "structural_peq"] #[lang = "structural_peq"]
trait StructuralPartialEq {} pub trait StructuralPartialEq {}
const fn drop<T: ~const Destruct>(_: T) {} // FIXME: const fn drop<T: ~const Destruct>(_: T) {}
#[rustc_const_stable_indirect]
#[rustc_intrinsic_must_be_overridden] #[rustc_intrinsic_must_be_overridden]
#[rustc_intrinsic] #[rustc_intrinsic]
const fn const_eval_select<ARG: Tuple, F, G, RET>( const fn const_eval_select<ARG: Tuple, F, G, RET>(
@ -536,10 +473,3 @@ where
{ {
loop {} loop {}
} }
fn test_const_eval_select() {
const fn const_fn() {}
fn rt_fn() {}
const_eval_select((), const_fn, rt_fn);
}

View File

@ -0,0 +1,20 @@
//@ aux-build:minicore.rs
//@ compile-flags: --crate-type=lib -Znext-solver -Cpanic=abort
#![feature(no_core, const_trait_impl)]
#![no_std]
#![no_core]
extern crate minicore;
use minicore::*;
struct Ty;
impl Deref for Ty {
type Target = ();
fn deref(&self) -> &Self::Target { &() }
}
const fn foo() {
*Ty;
//~^ ERROR the trait bound `Ty: ~const minicore::Deref` is not satisfied
}

View File

@ -0,0 +1,9 @@
error[E0277]: the trait bound `Ty: ~const minicore::Deref` is not satisfied
--> $DIR/minicore-deref-fail.rs:18:5
|
LL | *Ty;
| ^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,22 @@
//@ aux-build:minicore.rs
//@ compile-flags: --crate-type=lib -Znext-solver -Cpanic=abort
//@ check-pass
#![feature(no_core)]
#![no_std]
#![no_core]
#![feature(const_trait_impl)]
extern crate minicore;
use minicore::*;
struct Custom;
impl const Add for Custom {
type Output = ();
fn add(self, _other: Self) {}
}
const fn test_op() {
let _x = Add::add(1, 2);
let _y = Custom + Custom;
}

View File

@ -1,13 +0,0 @@
error: the compiler unexpectedly panicked. this is a bug.
query stack during panic:
#0 [typeck] type-checking `Clone::clone_from`
#1 [analysis] running analysis passes on this crate
end of query stack
error: the compiler unexpectedly panicked. this is a bug.
query stack during panic:
#0 [typeck] type-checking `test_const_eval_select`
#1 [analysis] running analysis passes on this crate
end of query stack