Skip to content

Fix Deref args when #[const_trait] is enabled #118386

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1648,7 +1648,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 @@ -2072,9 +2072,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 UnordMap<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 @@ -811,10 +811,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::ConstFn` must have host effect param"
)
});
ty::GenericArgs::identity_for_item(self, def_id).const_at(host_idx)
}
None => self.consts.true_,
Expand All @@ -828,15 +830,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 @@ -254,7 +254,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 @@ -4129,7 +4129,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,5 +1,9 @@
// check-pass
// FIXME(effects) this shouldn't pass
// known-bug: #110395
// 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 @@ -13,5 +17,4 @@ impl Foo for () {

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