Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Check lifetimes on existential types
  • Loading branch information
oli-obk committed Jul 18, 2018
1 parent 240fcdf commit feb139f
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 39 deletions.
1 change: 1 addition & 0 deletions src/librustc/infer/anon_types/mod.rs
Expand Up @@ -653,6 +653,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
let tcx = self.infcx.tcx;
value.fold_with(&mut BottomUpFolder {
tcx,
reg_op: |reg| reg,
fldop: |ty| {
if let ty::TyAnon(def_id, substs) = ty.sty {
// Check that this is `impl Trait` type is
Expand Down
14 changes: 11 additions & 3 deletions src/librustc/ty/fold.rs
Expand Up @@ -200,22 +200,30 @@ pub trait TypeVisitor<'tcx> : Sized {
///////////////////////////////////////////////////////////////////////////
// Some sample folders

pub struct BottomUpFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a, F>
where F: FnMut(Ty<'tcx>) -> Ty<'tcx>
pub struct BottomUpFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a, F, G>
where F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
{
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
pub fldop: F,
pub reg_op: G,
}

impl<'a, 'gcx, 'tcx, F> TypeFolder<'gcx, 'tcx> for BottomUpFolder<'a, 'gcx, 'tcx, F>
impl<'a, 'gcx, 'tcx, F, G> TypeFolder<'gcx, 'tcx> for BottomUpFolder<'a, 'gcx, 'tcx, F, G>
where F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
{
fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx }

fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
let t1 = ty.super_fold_with(self);
(self.fldop)(t1)
}

fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
let r = r.super_fold_with(self);
(self.reg_op)(r)
}
}

///////////////////////////////////////////////////////////////////////////
Expand Down
48 changes: 43 additions & 5 deletions src/librustc_typeck/check/wfcheck.rs
Expand Up @@ -575,9 +575,10 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>(
let anon_node_id = tcx.hir.as_local_node_id(def_id).unwrap();
if may_define_existential_type(tcx, fn_def_id, anon_node_id) {
trace!("check_existential_types may define. Generics: {:#?}", generics);
let mut seen: FxHashMap<_, Vec<_>> = FxHashMap();
for (subst, param) in substs.iter().zip(&generics.params) {
if let ty::subst::UnpackedKind::Type(ty) = subst.unpack() {
match ty.sty {
match subst.unpack() {
ty::subst::UnpackedKind::Type(ty) => match ty.sty {
ty::TyParam(..) => {},
// prevent `fn foo() -> Foo<u32>` from being defining
_ => {
Expand All @@ -597,11 +598,47 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>(
),
)
.emit();
return tcx.types.err;
},
} // match ty
} // if let Type = subst
}, // match ty
ty::subst::UnpackedKind::Lifetime(region) => {
let param_span = tcx.def_span(param.def_id);
if let ty::ReStatic = region {
tcx
.sess
.struct_span_err(
span,
"non-defining existential type use \
in defining scope",
)
.span_label(
param_span,
"cannot use static lifetime, use a bound lifetime \
instead or remove the lifetime parameter from the \
existential type",
)
.emit();
} else {
seen.entry(region).or_default().push(param_span);
}
},
} // match subst
} // for (subst, param)
for (_, spans) in seen {
if spans.len() > 1 {
tcx
.sess
.struct_span_err(
span,
"non-defining existential type use \
in defining scope",
).
span_note(
spans,
"lifetime used multiple times",
)
.emit();
}
}
} // if may_define_existential_type

// now register the bounds on the parameters of the existential type
Expand Down Expand Up @@ -631,6 +668,7 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>(
} // if let TyAnon
ty
},
reg_op: |reg| reg,
});
substituted_predicates
}
Expand Down
64 changes: 54 additions & 10 deletions src/librustc_typeck/check/writeback.rs
Expand Up @@ -389,16 +389,17 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() {
let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
let instantiated_ty = self.resolve(&anon_defn.concrete_ty, &node_id);
let mut definition_ty = self.fcx.infer_anon_definition_from_instantiation(
def_id,
anon_defn,
instantiated_ty,
);

let generics = self.tcx().generics_of(def_id);

// named existential type, not an impl trait
if generics.parent.is_none() {
let definition_ty = if generics.parent.is_some() {
// impl trait
self.fcx.infer_anon_definition_from_instantiation(
def_id,
anon_defn,
instantiated_ty,
)
} else {
// prevent
// * `fn foo<T>() -> Foo<T>`
// * `fn foo<T: Bound + Other>() -> Foo<T>`
Expand All @@ -411,9 +412,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
// fn foo<U>() -> Foo<U> { .. }
// ```
// figures out the concrete type with `U`, but the stored type is with `T`
definition_ty = definition_ty.fold_with(&mut BottomUpFolder {
instantiated_ty.fold_with(&mut BottomUpFolder {
tcx: self.tcx().global_tcx(),
fldop: |ty| {
trace!("checking type {:?}: {:#?}", ty, ty.sty);
// find a type parameter
if let ty::TyParam(..) = ty.sty {
// look it up in the substitution list
Expand Down Expand Up @@ -445,8 +447,50 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
}
ty
},
});
}
reg_op: |region| {
match region {
// ignore static regions
ty::ReStatic => region,
_ => {
trace!("checking {:?}", region);
for (subst, p) in anon_defn.substs.iter().zip(&generics.params) {
if let UnpackedKind::Lifetime(subst) = subst.unpack() {
if subst == region {
// found it in the substitution list, replace with the
// parameter from the existential type
let reg = ty::EarlyBoundRegion {
def_id: p.def_id,
index: p.index,
name: p.name,
};
trace!("replace {:?} with {:?}", region, reg);
return self.tcx().global_tcx()
.mk_region(ty::ReEarlyBound(reg));
}
}
}
trace!("anon_defn: {:#?}", anon_defn);
trace!("generics: {:#?}", generics);
self.tcx().sess
.struct_span_err(
span,
"non-defining existential type use in defining scope",
)
.span_label(
span,
format!(
"lifetime `{}` is part of concrete type but not used \
in parameter list of existential type",
region,
),
)
.emit();
self.tcx().global_tcx().mk_region(ty::ReStatic)
}
}
}
})
};

let old = self.tables.concrete_existential_types.insert(def_id, definition_ty);
if let Some(old) = old {
Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/existential_types/generic_duplicate_lifetime_param.rs
@@ -0,0 +1,20 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.


#![feature(existential_type)]

fn main() {}

existential type Two<'a, 'b>: std::fmt::Debug;

fn one<'a>(t: &'a ()) -> Two<'a, 'a> { //~ ERROR non-defining existential type use
t
}
@@ -0,0 +1,16 @@
error: non-defining existential type use in defining scope
--> $DIR/generic_duplicate_lifetime_param.rs:18:1
|
LL | / fn one<'a>(t: &'a ()) -> Two<'a, 'a> { //~ ERROR non-defining existential type use
LL | | t
LL | | }
| |_^
|
note: lifetime used multiple times
--> $DIR/generic_duplicate_lifetime_param.rs:16:22
|
LL | existential type Two<'a, 'b>: std::fmt::Debug;
| ^^ ^^

error: aborting due to previous error

20 changes: 20 additions & 0 deletions src/test/ui/existential_types/generic_duplicate_param_use.rs
@@ -0,0 +1,20 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.


#![feature(existential_type)]

fn main() {}

existential type Two<T, U>: 'static; //~ ERROR type parameter `U` is unused

fn one<T: 'static>(t: T) -> Two<T, T> {
t
}
@@ -0,0 +1,9 @@
error[E0091]: type parameter `U` is unused
--> $DIR/generic_duplicate_param_use.rs:16:25
|
LL | existential type Two<T, U>: 'static; //~ ERROR type parameter `U` is unused
| ^ unused type parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0091`.
21 changes: 21 additions & 0 deletions src/test/ui/existential_types/generic_lifetime_param.rs
@@ -0,0 +1,21 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-pass

#![feature(existential_type)]

fn main() {}

existential type Region<'a>: std::fmt::Debug;

fn region<'b>(a: &'b ()) -> Region<'b> {
a
}
Expand Up @@ -13,21 +13,23 @@

fn main() {}

mod boo2 {
mod boo {
pub existential type Boo: ::std::fmt::Debug;
fn bomp() -> Boo {
""
}
mod boo {
pub existential type Boo: ::std::fmt::Debug;
fn bomp() -> Boo {
""
}
}

// don't actually know the type here
// don't actually know the type here

fn bomp2() {
let _: &str = bomp(); //~ ERROR mismatched types
}
fn bomp2() {
let _: &str = bomp(); //~ ERROR mismatched types
}

fn bomp() -> boo::Boo {
"" //~ ERROR mismatched types
}
fn bomp() -> boo::Boo {
"" //~ ERROR mismatched types
}

fn bomp_loop() -> boo::Boo {
loop {}
}
@@ -1,19 +1,19 @@
error[E0308]: mismatched types
--> $DIR/no_revealing_outside_defining_module.rs:27:23
--> $DIR/no_revealing_outside_defining_module.rs:26:19
|
LL | let _: &str = bomp(); //~ ERROR mismatched types
| ^^^^^^ expected &str, found anonymized type
LL | let _: &str = bomp(); //~ ERROR mismatched types
| ^^^^^^ expected &str, found anonymized type
|
= note: expected type `&str`
found type `Boo`

error[E0308]: mismatched types
--> $DIR/no_revealing_outside_defining_module.rs:31:9
--> $DIR/no_revealing_outside_defining_module.rs:30:5
|
LL | fn bomp() -> boo::Boo {
| -------- expected `Boo` because of return type
LL | "" //~ ERROR mismatched types
| ^^ expected anonymized type, found reference
LL | fn bomp() -> boo::Boo {
| -------- expected `Boo` because of return type
LL | "" //~ ERROR mismatched types
| ^^ expected anonymized type, found reference
|
= note: expected type `Boo`
found type `&'static str`
Expand Down

0 comments on commit feb139f

Please sign in to comment.