-
-
Notifications
You must be signed in to change notification settings - Fork 14.8k
generic_const_args: allow paths to non type consts #155341
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -384,19 +384,46 @@ where | |
|
|
||
| // Finally we construct the actual value of the associated type. | ||
| let term = match goal.predicate.alias.kind(cx) { | ||
| ty::AliasTermKind::ProjectionTy { .. } => { | ||
| cx.type_of(target_item_def_id).map_bound(|ty| ty.into()) | ||
| ty::AliasTermKind::ProjectionTy { .. } => cx | ||
| .type_of(target_item_def_id) | ||
| .instantiate(cx, target_args) | ||
| .skip_norm_wip() | ||
| .into(), | ||
| ty::AliasTermKind::ProjectionConst { .. } | ||
| if cx.is_type_const(target_item_def_id) => | ||
| { | ||
| cx.const_of_item(target_item_def_id) | ||
| .instantiate(cx, target_args) | ||
| .skip_norm_wip() | ||
| .into() | ||
| } | ||
| ty::AliasTermKind::ProjectionConst { .. } => { | ||
| cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into()) | ||
| // target_args contains vars: | ||
| // - the Self type of the impl block is instantiated with fresh vars | ||
| // - the resulting type is eq'd against the actual Self type | ||
| // - the fresh vars are then used as target_args | ||
| // we need the actual args to run const eval, so we need to actually do the `eq` | ||
| // and figure out the args, so, call try_evaluate_added_goals | ||
| ecx.try_evaluate_added_goals()?; | ||
| // HACK(khyperia): this shouldn't be necessary, `try_evaluate_const` calls | ||
| // `resolve_vars_if_possible`. However, on failure, | ||
| // `evaluate_const_and_instantiate` then checks `has_non_region_infer` on the | ||
| // *pre*-`resolve_vars_if_possible` args, which, we want to return false if we | ||
| // successfully identified the actual type. | ||
| // Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics | ||
| // and HasInfers or something, and make evaluate_const_and_instantiate change | ||
| // its behavior based on that, rather than it checking `has_non_region_infer`. | ||
| let target_args = ecx.resolve_vars_if_possible(target_args); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @BoxyUwU this is annoying and gross (see HACK comment), |
||
| let uv = ty::UnevaluatedConst::new( | ||
| target_item_def_id.try_into().unwrap(), | ||
| target_args, | ||
| ); | ||
| return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv); | ||
| } | ||
| kind => panic!("expected projection, found {kind:?}"), | ||
| }; | ||
|
|
||
| ecx.instantiate_normalizes_to_term( | ||
| goal, | ||
| term.instantiate(cx, target_args).skip_norm_wip(), | ||
| ); | ||
| ecx.instantiate_normalizes_to_term(goal, term); | ||
| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) | ||
| }) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -545,10 +545,22 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( | |
| )); | ||
| } | ||
|
|
||
| let term: Term<'tcx> = if alias_term.kind(tcx).is_type() { | ||
| tcx.type_of(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip().into() | ||
| } else { | ||
| tcx.const_of_item(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip().into() | ||
| let term: Term<'tcx> = match alias_term.kind(tcx) { | ||
| ty::AliasTermKind::InherentTy { def_id } => { | ||
| tcx.type_of(def_id).instantiate(tcx, args).skip_norm_wip().into() | ||
| } | ||
| ty::AliasTermKind::InherentConst { def_id } if tcx.is_type_const(def_id) => { | ||
| tcx.const_of_item(def_id).instantiate(tcx, args).skip_norm_wip().into() | ||
| } | ||
| ty::AliasTermKind::InherentConst { .. } => { | ||
| // FIXME(gca): This is dead code at the moment. It should eventually call | ||
| // super::evaluate_const like projected consts do in confirm_impl_candidate in this | ||
| // file. However, how generic args are represented for IACs is up in the air right now. | ||
| // Will super::evaluate_const eventually take the inherent_args or the impl_args form of | ||
| // args? It might be either. | ||
| panic!("References to inherent associated consts should have been blocked"); | ||
| } | ||
| kind => panic!("expected inherent alias, found {kind:?}"), | ||
| }; | ||
|
|
||
| let mut term = selcx.infcx.resolve_vars_if_possible(term); | ||
|
|
@@ -613,11 +625,13 @@ pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>( | |
| alias_term.rebase_inherent_args_onto_impl(impl_args, tcx) | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| enum Projected<'tcx> { | ||
| Progress(Progress<'tcx>), | ||
| NoProgress(ty::Term<'tcx>), | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| struct Progress<'tcx> { | ||
| term: ty::Term<'tcx>, | ||
| obligations: PredicateObligations<'tcx>, | ||
|
|
@@ -648,7 +662,7 @@ impl<'tcx> Progress<'tcx> { | |
| /// IMPORTANT: | ||
| /// - `obligation` must be fully normalized | ||
| // FIXME(mgca): While this supports constants, it is only used for types by default right now | ||
| #[instrument(level = "info", skip(selcx))] | ||
| #[instrument(level = "info", ret, skip(selcx))] | ||
| fn project<'cx, 'tcx>( | ||
| selcx: &mut SelectionContext<'cx, 'tcx>, | ||
| obligation: &ProjectionTermObligation<'tcx>, | ||
|
|
@@ -2029,12 +2043,6 @@ fn confirm_impl_candidate<'cx, 'tcx>( | |
| let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); | ||
| let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node); | ||
|
|
||
| let term = if obligation.predicate.kind(tcx).is_type() { | ||
| tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into()) | ||
| } else { | ||
| tcx.const_of_item(assoc_term.item.def_id).map_bound(|ct| ct.into()) | ||
| }; | ||
|
|
||
| let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) { | ||
| let msg = "impl item and trait item have different parameters"; | ||
| let span = obligation.cause.span; | ||
|
|
@@ -2046,7 +2054,42 @@ fn confirm_impl_candidate<'cx, 'tcx>( | |
| Progress { term: err, obligations: nested } | ||
| } else { | ||
| assoc_term_own_obligations(selcx, obligation, &mut nested); | ||
| Progress { term: term.instantiate(tcx, args).skip_norm_wip(), obligations: nested } | ||
|
|
||
| let term = match obligation.predicate.kind(tcx) { | ||
| ty::AliasTermKind::ProjectionTy { .. } => { | ||
| tcx.type_of(assoc_term.item.def_id).instantiate(tcx, args).skip_norm_wip().into() | ||
| } | ||
| ty::AliasTermKind::ProjectionConst { .. } | ||
| if tcx.is_type_const(assoc_term.item.def_id) => | ||
| { | ||
| tcx.const_of_item(assoc_term.item.def_id) | ||
| .instantiate(tcx, args) | ||
| .skip_norm_wip() | ||
| .into() | ||
| } | ||
| ty::AliasTermKind::ProjectionConst { .. } => { | ||
| let uv = ty::UnevaluatedConst::new(assoc_term.item.def_id, args); | ||
| let ct = ty::Const::new_unevaluated(tcx, uv); | ||
| // We don't want to use super::evaluate_const, because that returns its parameter | ||
| // unchanged if it is too generic to evaluate. We are passing the `impl` form of the | ||
| // constant to evaluate_const (with generic arguments corresponding to the impl | ||
| // block), but we want to return the original, non-rebased, trait `Self` form of the | ||
| // constant (with generic arguments being the trait `Self` type) in Projected::NoProgress. | ||
| match super::try_evaluate_const(selcx.infcx, ct, param_env) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add a comment about why we're not just using |
||
| Ok(evaluated) => evaluated.into(), | ||
| Err( | ||
| super::EvaluateConstErr::EvaluationFailure(e) | ||
| | super::EvaluateConstErr::InvalidConstParamTy(e), | ||
| ) => ty::Const::new_error(tcx, e).into(), | ||
| Err(super::EvaluateConstErr::HasGenericsOrInfers) => { | ||
| return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx))); | ||
| } | ||
| } | ||
| } | ||
| kind => panic!("expected projection alias, found {kind:?}"), | ||
| }; | ||
|
|
||
| Progress { term, obligations: nested } | ||
| }; | ||
| Ok(Projected::Progress(progress)) | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized I should add a comment here pointing out it's intentionally instantiating to
goal.predicate.aliasrather than the passed inuv(very important distinction, for the same reasons as the old solver reason we're using try_evaluate_const instead of evaluate_const)View changes since the review