Skip to content

Commit

Permalink
Auto merge of #118386 - compiler-errors:const-deref, r=<try>
Browse files Browse the repository at this point in the history
Fix `Deref` args when `#[const_trait]` is enabled

r? `@fee1-dead`

This is still pretty broken, but putting this PR up so I don't lose the code in some `git stash` somewhere...
  • Loading branch information
bors committed Dec 18, 2023
2 parents 2a76340 + 38e0402 commit 4a90a2c
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 60 deletions.
22 changes: 18 additions & 4 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct Autoderef<'a, 'tcx> {
span: Span,
body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
host_effect_param: ty::Const<'tcx>,

// Current state:
state: AutoderefSnapshot<'tcx>,
Expand Down Expand Up @@ -109,14 +110,15 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
pub fn new(
infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_def_id: LocalDefId,
body_id: LocalDefId,
span: Span,
base_ty: Ty<'tcx>,
host_effect_param: ty::Const<'tcx>,
) -> Autoderef<'a, 'tcx> {
Autoderef {
infcx,
span,
body_id: body_def_id,
body_id,
param_env,
state: AutoderefSnapshot {
steps: vec![],
Expand All @@ -127,6 +129,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
},
include_raw_pointers: false,
silence_errors: false,
host_effect_param,
}
}

Expand All @@ -135,7 +138,18 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
let tcx = self.infcx.tcx;

// <ty as Deref>
let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
let deref_trait_def_id = tcx.lang_items().deref_trait()?;

// FIXME(effects): This is still broken, since we don't necessarily have a choice of
// `host = true` or `host = host` in `const` functions. This is also busted in `method_autoderef_steps`.
let deref_generics = self.infcx.tcx.generics_of(deref_trait_def_id);
let args = if deref_generics.host_effect_index.is_some() {
self.infcx.tcx.mk_args(&[ty.into(), self.host_effect_param.into()])
} else {
self.infcx.tcx.mk_args(&[ty.into()])
};

let trait_ref = ty::TraitRef::new(tcx, deref_trait_def_id, args);
let cause = traits::ObligationCause::misc(self.span, self.body_id);
let obligation = traits::Obligation::new(
tcx,
Expand All @@ -151,7 +165,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
tcx,
tcx.lang_items().deref_target()?,
[ty],
trait_ref.args,
))?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1644,7 +1644,14 @@ fn receiver_is_valid<'tcx>(
return true;
}

let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
let mut autoderef = Autoderef::new(
infcx,
wfcx.param_env,
wfcx.body_def_id,
span,
receiver_ty,
tcx.expected_host_effect_param_for_body(wfcx.body_def_id),
);

// The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`.
if arbitrary_self_types_enabled {
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_hir_typeck/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,24 @@ use std::iter;

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> {
Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
Autoderef::new(
self,
self.param_env,
self.body_id,
span,
base_ty,
self.tcx.expected_host_effect_param_for_body(self.body_id),
)
}

pub fn try_overloaded_deref(
&self,
span: Span,
base_ty: Ty<'tcx>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)
let callee = self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)?;
self.enforce_context_effects(span, callee.value.def_id, callee.value.args);
Some(callee)
}

/// Returns the adjustment steps.
Expand Down
47 changes: 26 additions & 21 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_middle::middle::stability;
use rustc_middle::query::Providers;
use rustc_middle::traits::query::type_op::MethodAutoderef;
use rustc_middle::traits::query::CanonicalMethodAutoderefGoal;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::AssocItem;
use rustc_middle::ty::GenericParamDefKind;
Expand All @@ -36,7 +38,6 @@ use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy
use rustc_trait_selection::traits::query::method_autoderef::{
CandidateStep, MethodAutoderefStepsResult,
};
use rustc_trait_selection::traits::query::CanonicalTyGoal;
use rustc_trait_selection::traits::NormalizeExt;
use rustc_trait_selection::traits::{self, ObligationCause};
use std::cell::RefCell;
Expand Down Expand Up @@ -371,34 +372,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>,
{
let mut orig_values = OriginalQueryValues::default();
let param_env_and_self_ty = self.canonicalize_query(
ParamEnvAnd { param_env: self.param_env, value: self_ty },
&mut orig_values,
);
let goal = MethodAutoderef {
self_ty,
host_effect_param: self.tcx.expected_host_effect_param_for_body(self.body_id),
};
let canonical_goal = self.canonicalize_query(self.param_env.and(goal), &mut orig_values);

let steps = match mode {
Mode::MethodCall => self.tcx.method_autoderef_steps(param_env_and_self_ty),
Mode::MethodCall => self.tcx.method_autoderef_steps(canonical_goal),
Mode::Path => self.probe(|_| {
// Mode::Path - the deref steps is "trivial". This turns
// our CanonicalQuery into a "trivial" QueryResponse. This
// is a bit inefficient, but I don't think that writing
// special handling for this "trivial case" is a good idea.

let infcx = &self.infcx;
let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) =
infcx.instantiate_canonical_with_fresh_inference_vars(
span,
&param_env_and_self_ty,
);
let (ParamEnvAnd { param_env: _, value: goal }, canonical_inference_vars) =
infcx.instantiate_canonical_with_fresh_inference_vars(span, &canonical_goal);
debug!(
"probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}",
param_env_and_self_ty, self_ty
"probe_op: Mode::Path, canonical_goal={:?} self_ty={:?}",
canonical_goal, self_ty
);
MethodAutoderefStepsResult {
steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
self_ty: self.make_query_response_ignoring_pending_obligations(
canonical_inference_vars,
self_ty,
goal.self_ty,
),
autoderefs: 0,
from_unsafe_deref: false,
Expand Down Expand Up @@ -510,17 +509,23 @@ pub fn provide(providers: &mut Providers) {

fn method_autoderef_steps<'tcx>(
tcx: TyCtxt<'tcx>,
goal: CanonicalTyGoal<'tcx>,
goal: CanonicalMethodAutoderefGoal<'tcx>,
) -> MethodAutoderefStepsResult<'tcx> {
debug!("method_autoderef_steps({:?})", goal);

let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
let ParamEnvAnd { param_env, value: self_ty } = goal;

let mut autoderef =
Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty)
.include_raw_pointers()
.silence_errors();
let ParamEnvAnd { param_env, value: MethodAutoderef { self_ty, host_effect_param } } = goal;

let mut autoderef = Autoderef::new(
infcx,
param_env,
hir::def_id::CRATE_DEF_ID,
DUMMY_SP,
self_ty,
host_effect_param,
)
.include_raw_pointers()
.silence_errors();
let mut reached_raw_pointer = false;
let mut steps: Vec<_> = autoderef
.by_ref()
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::query::plumbing::{
};
use crate::thir;
use crate::traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
CanonicalMethodAutoderefGoal, CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution,
};
Expand Down Expand Up @@ -2077,9 +2077,9 @@ rustc_queries! {
}

query method_autoderef_steps(
goal: CanonicalTyGoal<'tcx>
goal: CanonicalMethodAutoderefGoal<'tcx>
) -> MethodAutoderefStepsResult<'tcx> {
desc { "computing autoderef types for `{}`", goal.value.value }
desc { "computing autoderef types for `{}`", goal.value.value.self_ty }
}

query supported_target_features(_: CrateNum) -> &'tcx FxHashMap<String, Option<Symbol>> {
Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_middle/src/traits/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_span::Span;

pub mod type_op {
use crate::ty::fold::TypeFoldable;
use crate::ty::{Predicate, Ty, TyCtxt, UserType};
use crate::ty::{Const, Predicate, Ty, TyCtxt, UserType};
use std::fmt;

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
Expand Down Expand Up @@ -65,12 +65,23 @@ pub mod type_op {
Self { value }
}
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
pub struct MethodAutoderef<'tcx> {
pub self_ty: Ty<'tcx>,
/// Expected host effect value of the caller. For const fns, it's
/// some const param ty, and for normal functions, it's `true`.
pub host_effect_param: Const<'tcx>,
}
}

pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;

pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;

pub type CanonicalMethodAutoderefGoal<'tcx> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::MethodAutoderef<'tcx>>>;

pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>;

pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> =
Expand Down
14 changes: 12 additions & 2 deletions compiler/rustc_middle/src/ty/adjustment.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::ty::{self, Ty, TyCtxt};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::lang_items::LangItem;
use rustc_macros::HashStable;
use rustc_span::Span;
Expand Down Expand Up @@ -121,7 +122,12 @@ pub struct OverloadedDeref<'tcx> {

impl<'tcx> OverloadedDeref<'tcx> {
/// Get the zst function item type for this method call.
pub fn method_call(&self, tcx: TyCtxt<'tcx>, source: Ty<'tcx>) -> Ty<'tcx> {
pub fn method_call(
&self,
tcx: TyCtxt<'tcx>,
source: Ty<'tcx>,
caller_def_id: LocalDefId,
) -> Ty<'tcx> {
let trait_def_id = match self.mutbl {
hir::Mutability::Not => tcx.require_lang_item(LangItem::Deref, None),
hir::Mutability::Mut => tcx.require_lang_item(LangItem::DerefMut, None),
Expand All @@ -132,7 +138,11 @@ impl<'tcx> OverloadedDeref<'tcx> {
.find(|m| m.kind == ty::AssocKind::Fn)
.unwrap()
.def_id;
Ty::new_fn_def(tcx, method_def_id, [source])
Ty::new_fn_def(
tcx,
method_def_id,
tcx.with_opt_host_effect_param(caller_def_id, method_def_id, [source]),
)
}
}

Expand Down
27 changes: 14 additions & 13 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,10 +804,12 @@ impl<'tcx> TyCtxt<'tcx> {
self.consts.false_
}
Some(hir::ConstContext::ConstFn) => {
let host_idx = self
.generics_of(def_id)
.host_effect_index
.expect("ConstContext::Maybe must have host effect param");
let host_idx = self.generics_of(def_id).host_effect_index.unwrap_or_else(|| {
span_bug!(
self.def_span(def_id),
"item with `ConstContext::Maybe` must have host effect param"
)
});
ty::GenericArgs::identity_for_item(self, def_id).const_at(host_idx)
}
None => self.consts.true_,
Expand All @@ -821,15 +823,14 @@ impl<'tcx> TyCtxt<'tcx> {
callee_def_id: DefId,
args: impl IntoIterator<Item: Into<ty::GenericArg<'tcx>>>,
) -> ty::GenericArgsRef<'tcx> {
let generics = self.generics_of(callee_def_id);
assert_eq!(generics.parent, None);

let opt_const_param = generics
.host_effect_index
.is_some()
.then(|| ty::GenericArg::from(self.expected_host_effect_param_for_body(caller_def_id)));

self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param))
let mut args = args.into_iter();
ty::GenericArgs::for_item(self, callee_def_id, |param, _| {
if param.is_host_effect() {
self.expected_host_effect_param_for_body(caller_def_id).into()
} else {
args.next().unwrap().into()
}
})
}
}

Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_mir_build/src/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
let ref_str = self.temp(ref_str_ty, test.span);
let deref = tcx.require_lang_item(LangItem::Deref, None);
let method = trait_method(tcx, deref, sym::deref, [ty]);
let method = trait_method(
tcx,
deref,
sym::deref,
tcx.with_opt_host_effect_param(self.def_id, deref, [ty]),
);
let eq_block = self.cfg.start_new_block();
self.cfg.push_assign(
block,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ impl<'tcx> Cx<'tcx> {
Adjust::Deref(Some(deref)) => {
// We don't need to do call adjust_span here since
// deref coercions always start with a built-in deref.
let call = deref.method_call(self.tcx(), expr.ty);
let call = deref.method_call(self.tcx(), expr.ty, self.body_owner.expect_local());

expr = Expr {
temp_lifetime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3906,7 +3906,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {

// Extract `<U as Deref>::Target` assoc type and check that it is `T`
&& let Some(deref_target_did) = tcx.lang_items().deref_target()
&& let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_args(&[ty::GenericArg::from(found_ty)]))
&& let args = tcx.with_opt_host_effect_param(tcx.hir().enclosing_body_owner(expr.hir_id), deref_target_did, [found_ty])
&& let projection = Ty::new_projection(tcx,deref_target_did, args)
&& let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection)
&& obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation))
&& infcx.can_eq(param_env, deref_target, target_ty)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// known-bug: unknown
// failure-status: 101
// dont-check-compiler-stderr

// const closures don't have host params...

#![feature(const_closures, const_trait_impl, effects)]
#![allow(incomplete_features)]

Expand All @@ -11,5 +17,4 @@ impl Foo for () {

fn main() {
(const || { (()).foo() })();
//~^ ERROR: cannot call non-const fn
}
Loading

0 comments on commit 4a90a2c

Please sign in to comment.