Skip to content

Commit

Permalink
Document some opaque types code
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed Jun 7, 2020
1 parent ea998cb commit 51a6550
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 100 deletions.
4 changes: 2 additions & 2 deletions src/librustc_resolve/late/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ enum Elide {
Exact(Region),
/// Less or more than one lifetime were found, error on unspecified.
Error(Vec<ElisionFailureInfo>),
/// Forbid lifetime elision inside of a larger scope that does. For
/// example, in let position impl trait.
/// Forbid lifetime elision inside of a larger scope where it would be
/// permitted. For example, in let position impl trait.
Forbid,
}

Expand Down
14 changes: 11 additions & 3 deletions src/librustc_trait_selection/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {

let first_own_region = match opaque_defn.origin {
hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => {
// For these opaque types, only the item's own lifetime
// parameters are considered.
// We lower
//
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
//
// into
//
// type foo::<'p0..'pn>::Foo<'q0..'qm>
// fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
//
// For these types we onlt iterate over `'l0..lm` below.
tcx.generics_of(def_id).parent_count
}
// These opaque type inherit all lifetime parameters from their
// parent.
// parent, so we have to check them all.
hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::Misc => 0,
};

Expand Down
181 changes: 86 additions & 95 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,18 +801,14 @@ fn check_where_clauses<'tcx, 'fcx>(
traits::Obligation::new(cause, fcx.param_env, pred)
});

let mut predicates = predicates.instantiate_identity(fcx.tcx);
let predicates = predicates.instantiate_identity(fcx.tcx);

if let Some((mut return_ty, span)) = return_ty {
if return_ty.has_infer_types_or_consts() {
fcx.select_obligations_where_possible(false, |_| {});
return_ty = fcx.resolve_vars_if_possible(&return_ty);
}
let opaque_types = check_opaque_types(tcx, fcx, def_id.expect_local(), span, return_ty);
for _ in 0..opaque_types.len() {
predicates.spans.push(span);
}
predicates.predicates.extend(opaque_types);
check_opaque_types(tcx, fcx, def_id.expect_local(), span, return_ty);
}

let predicates = fcx.normalize_associated_types_in(span, &predicates);
Expand Down Expand Up @@ -884,113 +880,109 @@ fn check_opaque_types<'fcx, 'tcx>(
fn_def_id: LocalDefId,
span: Span,
ty: Ty<'tcx>,
) -> Vec<ty::Predicate<'tcx>> {
) {
trace!("check_opaque_types(ty={:?})", ty);
let mut substituted_predicates = Vec::new();
ty.fold_with(&mut ty::fold::BottomUpFolder {
tcx: fcx.tcx,
ty_op: |ty| {
if let ty::Opaque(def_id, substs) = ty.kind {
trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs);
let generics = tcx.generics_of(def_id);
// Only check named `impl Trait` types defined in this crate.
if !def_id.is_local() {

let opaque_hir_id = if let Some(local_id) = def_id.as_local() {
tcx.hir().as_local_hir_id(local_id)
} else {
// Opaque types from other crates won't have defining uses in this crate.
return ty;
}
let opaque_hir_id = tcx.hir().as_local_hir_id(def_id.expect_local());
};
if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(_), .. }) =
tcx.hir().expect_item(opaque_hir_id).kind
{
// Don't check return position impl trait.
// No need to check return position impl trait (RPIT)
// because for type and const parameters they are correct
// by construction: we convert
//
// fn foo<P0..Pn>() -> impl Trait
//
// into
//
// type Foo<P0...Pn>
// fn foo<P0..Pn>() -> Foo<P0...Pn>.
//
// For lifetime parameters we convert
//
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
//
// into
//
// type foo::<'p0..'pn>::Foo<'q0..'qm>
// fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
//
// which would error here on all of the `'static` args.
return ty;
}
if !may_define_opaque_type(tcx, fn_def_id, opaque_hir_id) {
return ty;
}
if may_define_opaque_type(tcx, fn_def_id, opaque_hir_id) {
trace!("check_opaque_types: may define, generics={:#?}", generics);
let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
for (i, arg) in substs.iter().enumerate() {
let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)),

GenericArgKind::Lifetime(region) => {
if let ty::ReStatic = region {
tcx.sess
.struct_span_err(
span,
"non-defining opaque type use in defining scope",
)
.span_label(
tcx.def_span(generics.param_at(i, tcx).def_id),
"cannot use static lifetime; use a bound lifetime \
trace!("check_opaque_types: may define, generics={:#?}", generics);
let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
for (i, arg) in substs.iter().enumerate() {
let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)),

GenericArgKind::Lifetime(region) => {
if let ty::ReStatic = region {
tcx.sess
.struct_span_err(
span,
"non-defining opaque type use in defining scope",
)
.span_label(
tcx.def_span(generics.param_at(i, tcx).def_id),
"cannot use static lifetime; use a bound lifetime \
instead or remove the lifetime parameter from the \
opaque type",
)
.emit();
continue;
}

true
)
.emit();
continue;
}

GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)),
};

if arg_is_param {
seen_params.entry(arg).or_default().push(i);
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = generics.param_at(i, tcx);
tcx.sess
.struct_span_err(
span,
"non-defining opaque type use in defining scope",
)
.span_note(
tcx.def_span(opaque_param.def_id),
&format!(
"used non-generic {} `{}` for generic parameter",
opaque_param.kind.descr(),
arg,
),
)
.emit();
}
} // for (arg, param)

for (_, indices) in seen_params {
if indices.len() > 1 {
let descr = generics.param_at(indices[0], tcx).kind.descr();
let spans: Vec<_> = indices
.into_iter()
.map(|i| tcx.def_span(generics.param_at(i, tcx).def_id))
.collect();
tcx.sess
.struct_span_err(
span,
"non-defining opaque type use in defining scope",
)
.span_note(spans, &format!("{} used multiple times", descr))
.emit();
true
}

GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)),
};

if arg_is_param {
seen_params.entry(arg).or_default().push(i);
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = generics.param_at(i, tcx);
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_note(
tcx.def_span(opaque_param.def_id),
&format!(
"used non-generic {} `{}` for generic parameter",
opaque_param.kind.descr(),
arg,
),
)
.emit();
}
} // if may_define_opaque_type

// Now register the bounds on the parameters of the opaque type
// so the parameters given by the function need to fulfill them.
//
// type Foo<T: Bar> = impl Baz + 'static;
// fn foo<U>() -> Foo<U> { .. *}
//
// becomes
//
// type Foo<T: Bar> = impl Baz + 'static;
// fn foo<U: Bar>() -> Foo<U> { .. *}
let predicates = tcx.predicates_of(def_id);
trace!("check_opaque_types: may define, predicates={:#?}", predicates,);
for &(pred, _) in predicates.predicates {
let substituted_pred = pred.subst(fcx.tcx, substs);
// Avoid duplication of predicates that contain no parameters, for example.
if !predicates.predicates.iter().any(|&(p, _)| p == substituted_pred) {
substituted_predicates.push(substituted_pred);
} // for (arg, param)

for (_, indices) in seen_params {
if indices.len() > 1 {
let descr = generics.param_at(indices[0], tcx).kind.descr();
let spans: Vec<_> = indices
.into_iter()
.map(|i| tcx.def_span(generics.param_at(i, tcx).def_id))
.collect();
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_note(spans, &format!("{} used multiple times", descr))
.emit();
}
}
} // if let Opaque
Expand All @@ -999,7 +991,6 @@ fn check_opaque_types<'fcx, 'tcx>(
lt_op: |lt| lt,
ct_op: |ct| ct,
});
substituted_predicates
}

const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \
Expand Down
10 changes: 10 additions & 0 deletions src/librustc_typeck/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,16 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
}
}

/// Retrieve the inferred concrete type for let position impl trait.
///
/// This is different to other kinds of impl trait because:
///
/// 1. We know which function contains the defining use (the function that
/// contains the let statement)
/// 2. We do not currently allow (free) lifetimes in the return type. `let`
/// statements in some statically unreachable code are removed from the MIR
/// by the time we borrow check, and it's not clear how we should handle
/// those.
fn let_position_impl_trait_type(tcx: TyCtxt<'_>, opaque_ty_id: LocalDefId) -> Ty<'_> {
let scope = tcx.hir().get_defining_scope(tcx.hir().as_local_hir_id(opaque_ty_id));
let scope_def_id = tcx.hir().local_def_id(scope);
Expand Down

0 comments on commit 51a6550

Please sign in to comment.