Auto merge of #92306 - Aaron1011:opaque-type-op, r=oli-obk

Improve opaque type higher-ranked region error message under NLL

Currently, any higher-ranked region errors involving opaque types
fall back to a generic "higher-ranked subtype error" message when
run under NLL. This PR adds better error message handling for this
case, giving us the same kinds of error messages that we currently
get without NLL:

```
error: implementation of `MyTrait` is not general enough
  --> $DIR/opaque-hrtb.rs:12:13
   |
LL | fn foo() -> impl for<'a> MyTrait<&'a str> {
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `MyTrait` is not general enough
   |
   = note: `impl MyTrait<&'2 str>` must implement `MyTrait<&'1 str>`, for any lifetime `'1`...
   = note: ...but it actually implements `MyTrait<&'2 str>`, for some specific lifetime `'2`

error: aborting due to previous error
```

To accomplish this, several different refactoring needed to be made:

* We now have a dedicated `InstantiateOpaqueType` struct which
implements `TypeOp`. This is used to invoke `instantiate_opaque_types`
during MIR type checking.
* `TypeOp` is refactored to pass around a `MirBorrowckCtxt`, which is
needed to report opaque type region errors.
* We no longer assume that all `TypeOp`s correspond to canonicalized
queries. This allows us to properly handle opaque type instantiation
(which does not occur in a query) as a `TypeOp`.
A new `ErrorInfo` associated type is used to determine what
additional information is used during higher-ranked region error
handling.
* The body of `try_extract_error_from_fulfill_cx`
has been moved out to a new function `try_extract_error_from_region_constraints`.
This allows us to re-use the same error reporting code between
canonicalized queries (which can extract region constraints directly
from a fresh `InferCtxt`) and opaque type handling (which needs to take
region constraints from the pre-existing `InferCtxt` that we use
throughout MIR borrow checking).
This commit is contained in:
bors 2022-02-09 09:41:48 +00:00
commit 1f0a96862a
14 changed files with 231 additions and 71 deletions

View file

@ -2,9 +2,13 @@ use rustc_errors::DiagnosticBuilder;
use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError; use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError;
use rustc_infer::infer::region_constraints::Constraint; use rustc_infer::infer::region_constraints::Constraint;
use rustc_infer::infer::region_constraints::RegionConstraintData;
use rustc_infer::infer::RegionVariableOrigin;
use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _}; use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _};
use rustc_infer::traits::{Normalized, ObligationCause, TraitEngine, TraitEngineExt}; use rustc_infer::traits::{Normalized, ObligationCause, TraitEngine, TraitEngineExt};
use rustc_middle::ty::error::TypeError; use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::RegionVid;
use rustc_middle::ty::UniverseIndex;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::query::type_op;
@ -78,6 +82,15 @@ crate trait ToUniverseInfo<'tcx> {
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx>; fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx>;
} }
impl<'tcx> ToUniverseInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(crate::type_check::InstantiateOpaqueType {
base_universe: Some(base_universe),
..self
})))
}
}
impl<'tcx> ToUniverseInfo<'tcx> impl<'tcx> ToUniverseInfo<'tcx>
for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>> for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>
{ {
@ -118,6 +131,12 @@ impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::Custo
} }
} }
impl<'tcx> ToUniverseInfo<'tcx> for ! {
fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
self
}
}
#[allow(unused_lifetimes)] #[allow(unused_lifetimes)]
trait TypeOpInfo<'tcx> { trait TypeOpInfo<'tcx> {
/// Returns an error to be reported if rerunning the type op fails to /// Returns an error to be reported if rerunning the type op fails to
@ -128,7 +147,7 @@ trait TypeOpInfo<'tcx> {
fn nice_error( fn nice_error(
&self, &self,
tcx: TyCtxt<'tcx>, mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
placeholder_region: ty::Region<'tcx>, placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>, error_region: Option<ty::Region<'tcx>>,
@ -175,7 +194,7 @@ trait TypeOpInfo<'tcx> {
debug!(?placeholder_region); debug!(?placeholder_region);
let span = cause.span; let span = cause.span;
let nice_error = self.nice_error(tcx, cause, placeholder_region, error_region); let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
if let Some(nice_error) = nice_error { if let Some(nice_error) = nice_error {
nice_error.buffer(&mut mbcx.errors_buffer); nice_error.buffer(&mut mbcx.errors_buffer);
@ -204,16 +223,16 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
fn nice_error( fn nice_error(
&self, &self,
tcx: TyCtxt<'tcx>, mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
placeholder_region: ty::Region<'tcx>, placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>, error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx>> { ) -> Option<DiagnosticBuilder<'tcx>> {
tcx.infer_ctxt().enter_with_canonical( mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
cause.span, cause.span,
&self.canonical_query, &self.canonical_query,
|ref infcx, key, _| { |ref infcx, key, _| {
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx); let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause); type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause);
try_extract_error_from_fulfill_cx( try_extract_error_from_fulfill_cx(
fulfill_cx, fulfill_cx,
@ -247,16 +266,16 @@ where
fn nice_error( fn nice_error(
&self, &self,
tcx: TyCtxt<'tcx>, mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
placeholder_region: ty::Region<'tcx>, placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>, error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx>> { ) -> Option<DiagnosticBuilder<'tcx>> {
tcx.infer_ctxt().enter_with_canonical( mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
cause.span, cause.span,
&self.canonical_query, &self.canonical_query,
|ref infcx, key, _| { |ref infcx, key, _| {
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx); let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
let mut selcx = SelectionContext::new(infcx); let mut selcx = SelectionContext::new(infcx);
@ -304,16 +323,16 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
fn nice_error( fn nice_error(
&self, &self,
tcx: TyCtxt<'tcx>, mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
cause: ObligationCause<'tcx>, cause: ObligationCause<'tcx>,
placeholder_region: ty::Region<'tcx>, placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>, error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx>> { ) -> Option<DiagnosticBuilder<'tcx>> {
tcx.infer_ctxt().enter_with_canonical( mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
cause.span, cause.span,
&self.canonical_query, &self.canonical_query,
|ref infcx, key, _| { |ref infcx, key, _| {
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx); let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span)) type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span))
.ok()?; .ok()?;
try_extract_error_from_fulfill_cx( try_extract_error_from_fulfill_cx(
@ -327,6 +346,39 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
} }
} }
impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
// FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
// and is only the fallback when the nice error fails. Consider improving this some more.
tcx.sess.struct_span_err(span, "higher-ranked lifetime error for opaque type!")
}
fn base_universe(&self) -> ty::UniverseIndex {
self.base_universe.unwrap()
}
fn nice_error(
&self,
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
_cause: ObligationCause<'tcx>,
placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx>> {
try_extract_error_from_region_constraints(
mbcx.infcx,
placeholder_region,
error_region,
self.region_constraints.as_ref().unwrap(),
// We're using the original `InferCtxt` that we
// started MIR borrowchecking with, so the region
// constraints have already been taken. Use the data from
// our `mbcx` instead.
|vid| mbcx.regioncx.var_infos[vid].origin,
|vid| mbcx.regioncx.var_infos[vid].universe,
)
}
}
#[instrument(skip(fulfill_cx, infcx), level = "debug")] #[instrument(skip(fulfill_cx, infcx), level = "debug")]
fn try_extract_error_from_fulfill_cx<'tcx>( fn try_extract_error_from_fulfill_cx<'tcx>(
mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>, mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>,
@ -334,15 +386,30 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
placeholder_region: ty::Region<'tcx>, placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>, error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx>> { ) -> Option<DiagnosticBuilder<'tcx>> {
let tcx = infcx.tcx;
// We generally shouldn't have errors here because the query was // We generally shouldn't have errors here because the query was
// already run, but there's no point using `delay_span_bug` // already run, but there's no point using `delay_span_bug`
// when we're going to emit an error here anyway. // when we're going to emit an error here anyway.
let _errors = fulfill_cx.select_all_or_error(infcx); let _errors = fulfill_cx.select_all_or_error(infcx);
let region_constraints = infcx.with_region_constraints(|r| r.clone());
try_extract_error_from_region_constraints(
infcx,
placeholder_region,
error_region,
&region_constraints,
|vid| infcx.region_var_origin(vid),
|vid| infcx.universe_of_region(infcx.tcx.mk_region(ty::ReVar(vid))),
)
}
let (sub_region, cause) = infcx.with_region_constraints(|region_constraints| { fn try_extract_error_from_region_constraints<'tcx>(
debug!("{:#?}", region_constraints); infcx: &InferCtxt<'_, 'tcx>,
placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>,
region_constraints: &RegionConstraintData<'tcx>,
mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
) -> Option<DiagnosticBuilder<'tcx>> {
let (sub_region, cause) =
region_constraints.constraints.iter().find_map(|(constraint, cause)| { region_constraints.constraints.iter().find_map(|(constraint, cause)| {
match *constraint { match *constraint {
Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => { Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
@ -350,12 +417,11 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
} }
// FIXME: Should this check the universe of the var? // FIXME: Should this check the universe of the var?
Constraint::VarSubReg(vid, sup) if sup == placeholder_region => { Constraint::VarSubReg(vid, sup) if sup == placeholder_region => {
Some((tcx.mk_region(ty::ReVar(vid)), cause.clone())) Some((infcx.tcx.mk_region(ty::ReVar(vid)), cause.clone()))
} }
_ => None, _ => None,
} }
}) })?;
})?;
debug!(?sub_region, "cause = {:#?}", cause); debug!(?sub_region, "cause = {:#?}", cause);
let nice_error = match (error_region, sub_region) { let nice_error = match (error_region, sub_region) {
@ -363,7 +429,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
infcx, infcx,
RegionResolutionError::SubSupConflict( RegionResolutionError::SubSupConflict(
vid, vid,
infcx.region_var_origin(vid), region_var_origin(vid),
cause.clone(), cause.clone(),
error_region, error_region,
cause.clone(), cause.clone(),
@ -380,8 +446,8 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
infcx, infcx,
RegionResolutionError::UpperBoundUniverseConflict( RegionResolutionError::UpperBoundUniverseConflict(
vid, vid,
infcx.region_var_origin(vid), region_var_origin(vid),
infcx.universe_of_region(sub_region), universe_of_region(vid),
cause.clone(), cause.clone(),
placeholder_region, placeholder_region,
), ),

View file

@ -5,6 +5,7 @@
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(let_else)] #![feature(let_else)]
#![feature(min_specialization)] #![feature(min_specialization)]
#![feature(never_type)]
#![feature(stmt_expr_attributes)] #![feature(stmt_expr_attributes)]
#![feature(trusted_step)] #![feature(trusted_step)]
#![feature(try_blocks)] #![feature(try_blocks)]

View file

@ -44,6 +44,7 @@ mod reverse_sccs;
pub mod values; pub mod values;
pub struct RegionInferenceContext<'tcx> { pub struct RegionInferenceContext<'tcx> {
pub var_infos: VarInfos,
/// Contains the definition for every region variable. Region /// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionVid`). The /// variables are identified by their index (`RegionVid`). The
/// definition contains information about where the region came /// definition contains information about where the region came
@ -266,7 +267,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
) -> Self { ) -> Self {
// Create a RegionDefinition for each inference variable. // Create a RegionDefinition for each inference variable.
let definitions: IndexVec<_, _> = var_infos let definitions: IndexVec<_, _> = var_infos
.into_iter() .iter()
.map(|info| RegionDefinition::new(info.universe, info.origin)) .map(|info| RegionDefinition::new(info.universe, info.origin))
.collect(); .collect();
@ -291,6 +292,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r))); Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
let mut result = Self { let mut result = Self {
var_infos,
definitions, definitions,
liveness_constraints, liveness_constraints,
constraints, constraints,

View file

@ -33,12 +33,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
) -> Fallible<R> ) -> Fallible<R>
where where
Op: type_op::TypeOp<'tcx, Output = R>, Op: type_op::TypeOp<'tcx, Output = R>,
Canonical<'tcx, Op>: ToUniverseInfo<'tcx>, Op::ErrorInfo: ToUniverseInfo<'tcx>,
{ {
let old_universe = self.infcx.universe(); let old_universe = self.infcx.universe();
let TypeOpOutput { output, constraints, canonicalized_query } = let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?;
op.fully_perform(self.infcx)?;
if let Some(data) = &constraints { if let Some(data) = &constraints {
self.push_region_constraints(locations, category, data); self.push_region_constraints(locations, category, data);
@ -47,8 +46,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let universe = self.infcx.universe(); let universe = self.infcx.universe();
if old_universe != universe { if old_universe != universe {
let universe_info = match canonicalized_query { let universe_info = match error_info {
Some(canonicalized_query) => canonicalized_query.to_universe_info(old_universe), Some(error_info) => error_info.to_universe_info(old_universe),
None => UniverseInfo::other(), None => UniverseInfo::other(),
}; };
for u in old_universe..universe { for u in old_universe..universe {

View file

@ -268,7 +268,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
TypeOpOutput { TypeOpOutput {
output: self.infcx.tcx.ty_error(), output: self.infcx.tcx.ty_error(),
constraints: None, constraints: None,
canonicalized_query: None, error_info: None,
} }
}); });
// Note: we need this in examples like // Note: we need this in examples like

View file

@ -17,6 +17,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_index::vec::{Idx, IndexVec}; use rustc_index::vec::{Idx, IndexVec};
use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::region_constraints::RegionConstraintData;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{ use rustc_infer::infer::{
InferCtxt, InferOk, LateBoundRegionConversionTime, NllRegionVariableOrigin, InferCtxt, InferOk, LateBoundRegionConversionTime, NllRegionVariableOrigin,
@ -39,9 +40,11 @@ use rustc_target::abi::VariantIdx;
use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::query::type_op;
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use rustc_trait_selection::traits::query::Fallible; use rustc_trait_selection::traits::query::Fallible;
use rustc_trait_selection::traits::{self, ObligationCause}; use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
use rustc_const_eval::transform::{ use rustc_const_eval::transform::{
check_consts::ConstCx, promote_consts::is_const_fn_in_array_repeat_expression, check_consts::ConstCx, promote_consts::is_const_fn_in_array_repeat_expression,
@ -2637,3 +2640,32 @@ impl NormalizeLocation for Location {
Locations::Single(self) Locations::Single(self)
} }
} }
/// Runs `infcx.instantiate_opaque_types`. Unlike other `TypeOp`s,
/// this is not canonicalized - it directly affects the main `InferCtxt`
/// that we use during MIR borrowchecking.
#[derive(Debug)]
pub(super) struct InstantiateOpaqueType<'tcx> {
pub base_universe: Option<ty::UniverseIndex>,
pub region_constraints: Option<RegionConstraintData<'tcx>>,
pub obligation: PredicateObligation<'tcx>,
}
impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
type Output = ();
/// We use this type itself to store the information used
/// when reporting errors. Since this is not a query, we don't
/// re-run anything during error reporting - we just use the information
/// we saved to help extract an error from the already-existing region
/// constraints in our `InferCtxt`
type ErrorInfo = InstantiateOpaqueType<'tcx>;
fn fully_perform(mut self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
let (mut output, region_constraints) = scrape_region_constraints(infcx, || {
Ok(InferOk { value: (), obligations: vec![self.obligation.clone()] })
})?;
self.region_constraints = Some(region_constraints);
output.error_info = Some(self);
Ok(output)
}
}

View file

@ -1,5 +1,5 @@
use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate};
use rustc_infer::infer::{InferOk, NllRegionVariableOrigin}; use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_middle::mir::ConstraintCategory; use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::relate::TypeRelation;
@ -9,7 +9,7 @@ use rustc_trait_selection::traits::query::Fallible;
use crate::constraints::OutlivesConstraint; use crate::constraints::OutlivesConstraint;
use crate::diagnostics::UniverseInfo; use crate::diagnostics::UniverseInfo;
use crate::type_check::{CustomTypeOp, Locations, TypeChecker}; use crate::type_check::{InstantiateOpaqueType, Locations, TypeChecker};
impl<'a, 'tcx> TypeChecker<'a, 'tcx> { impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
/// Adds sufficient constraints to ensure that `a R b` where `R` depends on `v`: /// Adds sufficient constraints to ensure that `a R b` where `R` depends on `v`:
@ -146,21 +146,18 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
.fully_perform_op( .fully_perform_op(
self.locations, self.locations,
self.category, self.category,
CustomTypeOp::new( InstantiateOpaqueType {
|infcx| { obligation: self.type_checker.infcx.opaque_ty_obligation(
Ok(InferOk { a,
value: (), b,
obligations: vec![infcx.opaque_ty_obligation( a_is_expected,
a, param_env,
b, cause,
a_is_expected, ),
param_env, // These fields are filled in during exectuion of the operation
cause, base_universe: None,
)], region_constraints: None,
}) },
},
|| "register_opaque_type".to_string(),
),
) )
.unwrap(); .unwrap();
} }

View file

@ -22,6 +22,7 @@
#![feature(control_flow_enum)] #![feature(control_flow_enum)]
#![feature(min_specialization)] #![feature(min_specialization)]
#![feature(label_break_value)] #![feature(label_break_value)]
#![feature(backtrace)]
#![recursion_limit = "512"] // For rustdoc #![recursion_limit = "512"] // For rustdoc
#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]

View file

@ -4,6 +4,7 @@ use crate::traits::engine::TraitEngineExt as _;
use crate::traits::query::type_op::TypeOpOutput; use crate::traits::query::type_op::TypeOpOutput;
use crate::traits::query::Fallible; use crate::traits::query::Fallible;
use crate::traits::TraitEngine; use crate::traits::TraitEngine;
use rustc_infer::infer::region_constraints::RegionConstraintData;
use rustc_infer::traits::TraitEngineExt as _; use rustc_infer::traits::TraitEngineExt as _;
use rustc_span::source_map::DUMMY_SP; use rustc_span::source_map::DUMMY_SP;
@ -31,6 +32,9 @@ where
G: Fn() -> String, G: Fn() -> String,
{ {
type Output = R; type Output = R;
/// We can't do any custom error reporting for `CustomTypeOp`, so
/// we can use `!` to enforce that the implementation never provides it.
type ErrorInfo = !;
/// Processes the operation and all resulting obligations, /// Processes the operation and all resulting obligations,
/// returning the final result along with any region constraints /// returning the final result along with any region constraints
@ -40,7 +44,7 @@ where
info!("fully_perform({:?})", self); info!("fully_perform({:?})", self);
} }
scrape_region_constraints(infcx, || (self.closure)(infcx)) Ok(scrape_region_constraints(infcx, || (self.closure)(infcx))?.0)
} }
} }
@ -55,10 +59,10 @@ where
/// Executes `op` and then scrapes out all the "old style" region /// Executes `op` and then scrapes out all the "old style" region
/// constraints that result, creating query-region-constraints. /// constraints that result, creating query-region-constraints.
fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
infcx: &InferCtxt<'_, 'tcx>, infcx: &InferCtxt<'_, 'tcx>,
op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>, op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
) -> Fallible<TypeOpOutput<'tcx, Op>> { ) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> {
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
// During NLL, we expect that nobody will register region // During NLL, we expect that nobody will register region
@ -97,12 +101,18 @@ fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
); );
if region_constraints.is_empty() { if region_constraints.is_empty() {
Ok(TypeOpOutput { output: value, constraints: None, canonicalized_query: None }) Ok((
TypeOpOutput { output: value, constraints: None, error_info: None },
region_constraint_data,
))
} else { } else {
Ok(TypeOpOutput { Ok((
output: value, TypeOpOutput {
constraints: Some(Rc::new(region_constraints)), output: value,
canonicalized_query: None, constraints: Some(Rc::new(region_constraints)),
}) error_info: None,
},
region_constraint_data,
))
} }
} }

View file

@ -28,6 +28,7 @@ pub use rustc_middle::traits::query::type_op::*;
/// cannot be completed). /// cannot be completed).
pub trait TypeOp<'tcx>: Sized + fmt::Debug { pub trait TypeOp<'tcx>: Sized + fmt::Debug {
type Output; type Output;
type ErrorInfo;
/// Processes the operation and all resulting obligations, /// Processes the operation and all resulting obligations,
/// returning the final result along with any region constraints /// returning the final result along with any region constraints
@ -41,9 +42,8 @@ pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> {
pub output: Op::Output, pub output: Op::Output,
/// Any region constraints from performing the type op. /// Any region constraints from performing the type op.
pub constraints: Option<Rc<QueryRegionConstraints<'tcx>>>, pub constraints: Option<Rc<QueryRegionConstraints<'tcx>>>,
/// The canonicalized form of the query. /// Used for error reporting to be able to rerun the query
/// This for error reporting to be able to rerun the query. pub error_info: Option<Op::ErrorInfo>,
pub canonicalized_query: Option<Canonical<'tcx, Op>>,
} }
/// "Query type ops" are type ops that are implemented using a /// "Query type ops" are type ops that are implemented using a
@ -119,10 +119,11 @@ where
Q: QueryTypeOp<'tcx>, Q: QueryTypeOp<'tcx>,
{ {
type Output = Q::QueryResponse; type Output = Q::QueryResponse;
type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>;
fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
let mut region_constraints = QueryRegionConstraints::default(); let mut region_constraints = QueryRegionConstraints::default();
let (output, canonicalized_query, mut obligations, _) = let (output, error_info, mut obligations, _) =
Q::fully_perform_into(self, infcx, &mut region_constraints)?; Q::fully_perform_into(self, infcx, &mut region_constraints)?;
// Typically, instantiating NLL query results does not // Typically, instantiating NLL query results does not
@ -160,6 +161,6 @@ where
let region_constraints = let region_constraints =
if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) }; if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) };
Ok(TypeOpOutput { output, constraints: region_constraints, canonicalized_query }) Ok(TypeOpOutput { output, constraints: region_constraints, error_info })
} }
} }

View file

@ -1,14 +1,20 @@
error: higher-ranked subtype error error: implementation of `Hrtb` is not general enough
--> $DIR/issue-88236-2.rs:17:5 --> $DIR/issue-88236-2.rs:17:5
| |
LL | &() LL | &()
| ^^^ | ^^^ implementation of `Hrtb` is not general enough
|
= note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
= note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
error: higher-ranked subtype error error: implementation of `Hrtb` is not general enough
--> $DIR/issue-88236-2.rs:17:5 --> $DIR/issue-88236-2.rs:17:5
| |
LL | &() LL | &()
| ^^^ | ^^^ implementation of `Hrtb` is not general enough
|
= note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
= note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
error: lifetime may not live long enough error: lifetime may not live long enough
--> $DIR/issue-88236-2.rs:20:5 --> $DIR/issue-88236-2.rs:20:5
@ -23,17 +29,23 @@ help: to allow this `impl Trait` to capture borrowed data with lifetime `'b`, ad
LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b { LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b {
| ++++ | ++++
error: higher-ranked subtype error error: implementation of `Hrtb` is not general enough
--> $DIR/issue-88236-2.rs:20:5 --> $DIR/issue-88236-2.rs:20:5
| |
LL | x LL | x
| ^ | ^ implementation of `Hrtb` is not general enough
|
= note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
= note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
error: higher-ranked subtype error error: implementation of `Hrtb` is not general enough
--> $DIR/issue-88236-2.rs:20:5 --> $DIR/issue-88236-2.rs:20:5
| |
LL | x LL | x
| ^ | ^ implementation of `Hrtb` is not general enough
|
= note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`...
= note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
error: aborting due to 5 previous errors error: aborting due to 5 previous errors

View file

@ -0,0 +1,16 @@
#![feature(nll)]
trait MyTrait<T> {}
struct Foo;
impl<T> MyTrait<T> for Foo {}
fn bar<Input>() -> impl MyTrait<Input> {
Foo
}
fn foo() -> impl for<'a> MyTrait<&'a str> {
bar() //~ ERROR implementation of `MyTrait` is not general enough
}
fn main() {}

View file

@ -0,0 +1,11 @@
error: implementation of `MyTrait` is not general enough
--> $DIR/opaque-hrtb.rs:13:5
|
LL | bar()
| ^^^^^ implementation of `MyTrait` is not general enough
|
= note: `impl MyTrait<&'2 str>` must implement `MyTrait<&'1 str>`, for any lifetime `'1`...
= note: ...but it actually implements `MyTrait<&'2 str>`, for some specific lifetime `'2`
error: aborting due to previous error

View file

@ -1,14 +1,26 @@
error: higher-ranked subtype error error[E0308]: mismatched types
--> $DIR/issue-57611-trait-alias.rs:20:9
|
LL | |x| x
| ^^^^^ one type is more general than the other
|
= note: expected type `for<'r> Fn<(&'r X,)>`
found type `Fn<(&X,)>`
note: this closure does not fulfill the lifetime requirements
--> $DIR/issue-57611-trait-alias.rs:20:9 --> $DIR/issue-57611-trait-alias.rs:20:9
| |
LL | |x| x LL | |x| x
| ^^^^^ | ^^^^^
error: higher-ranked subtype error error: implementation of `FnOnce` is not general enough
--> $DIR/issue-57611-trait-alias.rs:20:9 --> $DIR/issue-57611-trait-alias.rs:20:9
| |
LL | |x| x LL | |x| x
| ^^^^^ | ^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 X) -> &X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2`
error: aborting due to 2 previous errors error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.