Add impl Trait and dyn Trait types

- refactor bounds handling in the AST a bit
 - add HIR for bounds
 - add `Ty::Dyn` and `Ty::Opaque` variants and lower `dyn Trait` / `impl Trait`
   syntax to them
This commit is contained in:
Florian Diebold 2019-08-13 23:09:08 +02:00
parent 08e5d394df
commit 16a7d8cc85
8 changed files with 366 additions and 42 deletions

View File

@ -11,7 +11,7 @@ use crate::{
db::{AstDatabase, DefDatabase, HirDatabase},
name::SELF_TYPE,
path::Path,
type_ref::TypeRef,
type_ref::{TypeBound, TypeRef},
AdtDef, AsName, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct,
Trait, TypeAlias, Union,
};
@ -35,10 +35,12 @@ pub struct GenericParams {
/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
/// It might still result in multiple actual predicates though, because of
/// associated type bindings like `Iterator<Item = u32>`.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct WherePredicate {
pub(crate) type_ref: TypeRef,
pub(crate) trait_ref: Path,
pub(crate) bound: TypeBound,
}
// FIXME: consts can have type parameters from their parents (i.e. associated consts of traits)
@ -143,18 +145,8 @@ impl GenericParams {
// FIXME: remove this bound
return;
}
let path = bound
.type_ref()
.and_then(|tr| match tr {
ast::TypeRef::PathType(path) => path.path(),
_ => None,
})
.and_then(Path::from_ast);
let path = match path {
Some(p) => p,
None => return,
};
self.where_predicates.push(WherePredicate { type_ref, trait_ref: path });
let bound = TypeBound::from_ast(bound);
self.where_predicates.push(WherePredicate { type_ref, bound });
}
pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> {

View File

@ -161,14 +161,28 @@ pub enum Ty {
name: Name,
},
/// A bound type variable. Only used during trait resolution to represent
/// Chalk variables.
/// A bound type variable. Used during trait resolution to represent Chalk
/// variables, and in `Dyn` and `Opaque` bounds to represent the `Self` type.
Bound(u32),
/// A type variable used during type checking. Not to be confused with a
/// type parameter.
Infer(InferTy),
/// A trait object (`dyn Trait` or bare `Trait` in pre-2018 Rust).
///
/// The predicates are quantified over the `Self` type, i.e. `Ty::Bound(0)`
/// represents the `Self` type inside the bounds. This is currently
/// implicit; Chalk has the `Binders` struct to make it explicit, but it
/// didn't seem worth the overhead yet.
Dyn(Arc<[GenericPredicate]>),
/// An opaque type (`impl Trait`).
///
/// The predicates are quantified over the `Self` type; see `Ty::Dyn` for
/// more.
Opaque(Arc<[GenericPredicate]>),
/// A placeholder for a type which could not be computed; this is propagated
/// to avoid useless error messages. Doubles as a placeholder where type
/// variables are inserted before type checking, since we want to try to
@ -194,6 +208,12 @@ impl Substs {
Substs(self.0.iter().cloned().take(n).collect::<Vec<_>>().into())
}
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
for t in self.0.iter() {
t.walk(f);
}
}
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
// Without an Arc::make_mut_slice, we can't avoid the clone here:
let mut v: Vec<_> = self.0.iter().cloned().collect();
@ -270,6 +290,14 @@ impl TraitRef {
});
self
}
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
self.substs.walk(f);
}
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
self.substs.walk_mut(f);
}
}
/// Like `generics::WherePredicate`, but with resolved types: A condition on the
@ -299,6 +327,20 @@ impl GenericPredicate {
GenericPredicate::Error => self,
}
}
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
match self {
GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f),
GenericPredicate::Error => {}
}
}
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
match self {
GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut(f),
GenericPredicate::Error => {}
}
}
}
/// Basically a claim (currently not validated / checked) that the contained
@ -386,6 +428,11 @@ impl Ty {
t.walk(f);
}
}
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
for p in predicates.iter() {
p.walk(f);
}
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
@ -402,6 +449,13 @@ impl Ty {
Ty::UnselectedProjection(p_ty) => {
p_ty.parameters.walk_mut(f);
}
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
let mut v: Vec<_> = predicates.iter().cloned().collect();
for p in &mut v {
p.walk_mut(f);
}
*predicates = v.into();
}
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
}
f(self);
@ -669,6 +723,28 @@ impl HirDisplay for Ty {
Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?,
Ty::Param { name, .. } => write!(f, "{}", name)?,
Ty::Bound(idx) => write!(f, "?{}", idx)?,
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
match self {
Ty::Dyn(_) => write!(f, "dyn ")?,
Ty::Opaque(_) => write!(f, "impl ")?,
_ => unreachable!(),
};
// looping by hand here just to format the bounds in a slightly nicer way
let mut first = true;
for p in predicates.iter() {
if !first {
write!(f, " + ")?;
}
first = false;
match p {
// don't show the $0 self type
GenericPredicate::Implemented(trait_ref) => {
trait_ref.hir_fmt_ext(f, false)?
}
GenericPredicate::Error => p.hir_fmt(f)?,
}
}
}
Ty::Unknown => write!(f, "{{unknown}}")?,
Ty::Infer(..) => write!(f, "_")?,
}
@ -676,14 +752,16 @@ impl HirDisplay for Ty {
}
}
impl HirDisplay for TraitRef {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
write!(
f,
"{}: {}",
self.substs[0].display(f.db),
self.trait_.name(f.db).unwrap_or_else(Name::missing)
)?;
impl TraitRef {
fn hir_fmt_ext(
&self,
f: &mut HirFormatter<impl HirDatabase>,
with_self_ty: bool,
) -> fmt::Result {
if with_self_ty {
write!(f, "{}: ", self.substs[0].display(f.db),)?;
}
write!(f, "{}", self.trait_.name(f.db).unwrap_or_else(Name::missing))?;
if self.substs.len() > 1 {
write!(f, "<")?;
f.write_joined(&self.substs[1..], ", ")?;
@ -693,6 +771,28 @@ impl HirDisplay for TraitRef {
}
}
impl HirDisplay for TraitRef {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
self.hir_fmt_ext(f, true)
}
}
impl HirDisplay for &GenericPredicate {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
HirDisplay::hir_fmt(*self, f)
}
}
impl HirDisplay for GenericPredicate {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
match self {
GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
GenericPredicate::Error => write!(f, "{{error}}")?,
}
Ok(())
}
}
impl HirDisplay for Obligation {
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
match self {

View File

@ -17,7 +17,7 @@ use crate::{
path::{GenericArg, PathSegment},
resolve::{Resolution, Resolver},
ty::AdtDef,
type_ref::TypeRef,
type_ref::{TypeBound, TypeRef},
BuiltinType, Const, Enum, EnumVariant, Function, HirDatabase, ModuleDef, Path, Static, Struct,
StructField, Trait, TypeAlias, Union,
};
@ -58,6 +58,22 @@ impl Ty {
let sig = Substs(inner_tys.into());
Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig)
}
TypeRef::DynTrait(bounds) => {
let self_ty = Ty::Bound(0);
let predicates = bounds
.iter()
.map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone()))
.collect::<Vec<_>>();
Ty::Dyn(predicates.into())
}
TypeRef::ImplTrait(bounds) => {
let self_ty = Ty::Bound(0);
let predicates = bounds
.iter()
.map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone()))
.collect::<Vec<_>>();
Ty::Opaque(predicates.into())
}
TypeRef::Error => Ty::Unknown,
}
}
@ -310,13 +326,46 @@ impl TraitRef {
TraitRef { trait_, substs }
}
pub(crate) fn for_where_predicate(
pub(crate) fn from_where_predicate(
db: &impl HirDatabase,
resolver: &Resolver,
pred: &WherePredicate,
) -> Option<TraitRef> {
let self_ty = Ty::from_hir(db, resolver, &pred.type_ref);
TraitRef::from_path(db, resolver, &pred.trait_ref, Some(self_ty))
TraitRef::from_type_bound(db, resolver, &pred.bound, self_ty)
}
pub(crate) fn from_type_bound(
db: &impl HirDatabase,
resolver: &Resolver,
bound: &TypeBound,
self_ty: Ty,
) -> Option<TraitRef> {
match bound {
TypeBound::Path(path) => TraitRef::from_path(db, resolver, path, Some(self_ty)),
TypeBound::Error => None,
}
}
}
impl GenericPredicate {
pub(crate) fn from_where_predicate(
db: &impl HirDatabase,
resolver: &Resolver,
where_predicate: &WherePredicate,
) -> GenericPredicate {
TraitRef::from_where_predicate(db, &resolver, where_predicate)
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
}
pub(crate) fn from_type_bound(
db: &impl HirDatabase,
resolver: &Resolver,
bound: &TypeBound,
self_ty: Ty,
) -> GenericPredicate {
TraitRef::from_type_bound(db, &resolver, bound, self_ty)
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
}
}
@ -376,10 +425,7 @@ pub(crate) fn trait_env(
) -> Arc<super::TraitEnvironment> {
let predicates = resolver
.where_predicates_in_scope()
.map(|pred| {
TraitRef::for_where_predicate(db, &resolver, pred)
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
})
.map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred))
.collect::<Vec<_>>();
Arc::new(super::TraitEnvironment { predicates })
@ -393,10 +439,7 @@ pub(crate) fn generic_predicates_query(
let resolver = def.resolver(db);
let predicates = resolver
.where_predicates_in_scope()
.map(|pred| {
TraitRef::for_where_predicate(db, &resolver, pred)
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
})
.map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred))
.collect::<Vec<_>>();
predicates.into()
}

View File

@ -3273,6 +3273,126 @@ fn test<T: ApplyL>(t: T) {
assert_eq!(t, "{unknown}");
}
#[test]
fn impl_trait() {
assert_snapshot_matches!(
infer(r#"
trait Trait<T> {
fn foo(&self) -> T;
}
fn bar() -> impl Trait<u64> {}
fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
x;
y;
let z = bar();
x.foo();
y.foo();
z.foo();
}
"#),
@r###"
[30; 34) 'self': &Self
[72; 74) '{}': ()
[84; 85) 'x': impl Trait<u64>
[104; 105) 'y': &impl Trait<u64>
[125; 200) '{ ...o(); }': ()
[131; 132) 'x': impl Trait<u64>
[138; 139) 'y': &impl Trait<u64>
[149; 150) 'z': impl Trait<u64>
[153; 156) 'bar': fn bar() -> impl Trait<u64>
[153; 158) 'bar()': impl Trait<u64>
[164; 165) 'x': impl Trait<u64>
[164; 171) 'x.foo()': {unknown}
[177; 178) 'y': &impl Trait<u64>
[177; 184) 'y.foo()': {unknown}
[190; 191) 'z': impl Trait<u64>
[190; 197) 'z.foo()': {unknown}
"###
);
}
#[test]
fn dyn_trait() {
assert_snapshot_matches!(
infer(r#"
trait Trait<T> {
fn foo(&self) -> T;
}
fn bar() -> dyn Trait<u64> {}
fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
x;
y;
let z = bar();
x.foo();
y.foo();
z.foo();
}
"#),
@r###"
[30; 34) 'self': &Self
[71; 73) '{}': ()
[83; 84) 'x': dyn Trait<u64>
[102; 103) 'y': &dyn Trait<u64>
[122; 197) '{ ...o(); }': ()
[128; 129) 'x': dyn Trait<u64>
[135; 136) 'y': &dyn Trait<u64>
[146; 147) 'z': dyn Trait<u64>
[150; 153) 'bar': fn bar() -> dyn Trait<u64>
[150; 155) 'bar()': dyn Trait<u64>
[161; 162) 'x': dyn Trait<u64>
[161; 168) 'x.foo()': {unknown}
[174; 175) 'y': &dyn Trait<u64>
[174; 181) 'y.foo()': {unknown}
[187; 188) 'z': dyn Trait<u64>
[187; 194) 'z.foo()': {unknown}
"###
);
}
#[test]
fn dyn_trait_bare() {
assert_snapshot_matches!(
infer(r#"
trait Trait {
fn foo(&self) -> u64;
}
fn bar() -> Trait {}
fn test(x: Trait, y: &Trait) -> u64 {
x;
y;
let z = bar();
x.foo();
y.foo();
z.foo();
}
"#),
@r###"
[27; 31) 'self': &Self
[61; 63) '{}': ()
[73; 74) 'x': {unknown}
[83; 84) 'y': &{unknown}
[101; 176) '{ ...o(); }': ()
[107; 108) 'x': {unknown}
[114; 115) 'y': &{unknown}
[125; 126) 'z': {unknown}
[129; 132) 'bar': fn bar() -> {unknown}
[129; 134) 'bar()': {unknown}
[140; 141) 'x': {unknown}
[140; 147) 'x.foo()': {unknown}
[153; 154) 'y': &{unknown}
[153; 160) 'y.foo()': {unknown}
[166; 167) 'z': {unknown}
[166; 173) 'z.foo()': {unknown}
"###
);
}
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
let file = db.parse(pos.file_id).ok().unwrap();
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();

View File

@ -80,7 +80,9 @@ impl ToChalk for Ty {
// FIXME this is clearly incorrect, but probably not too incorrect
// and I'm not sure what to actually do with Ty::Unknown
// maybe an alternative would be `for<T> T`? (meaningless in rust, but expressible in chalk's Ty)
Ty::Unknown => {
//
// FIXME also dyn and impl Trait are currently handled like Unknown because Chalk doesn't have them yet
Ty::Unknown | Ty::Dyn(_) | Ty::Opaque(_) => {
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::max_value() }.to_ty()
}
}

View File

@ -1,7 +1,7 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
use ra_syntax::ast::{self, TypeAscriptionOwner};
use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner};
use crate::Path;
@ -49,8 +49,16 @@ pub enum TypeRef {
/// A fn pointer. Last element of the vector is the return type.
Fn(Vec<TypeRef>),
// For
// ImplTrait,
// DynTrait,
ImplTrait(Vec<TypeBound>),
DynTrait(Vec<TypeBound>),
Error,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeBound {
Path(Path),
// also for<> bounds
// also Lifetimes
Error,
}
@ -95,8 +103,12 @@ impl TypeRef {
}
// for types are close enough for our purposes to the inner type for now...
ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
ast::TypeRef::ImplTraitType(_inner) => TypeRef::Error,
ast::TypeRef::DynTraitType(_inner) => TypeRef::Error,
ast::TypeRef::ImplTraitType(inner) => {
TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list()))
}
ast::TypeRef::DynTraitType(inner) => {
TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list()))
}
}
}
@ -112,3 +124,32 @@ impl TypeRef {
TypeRef::Tuple(Vec::new())
}
}
pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>) -> Vec<TypeBound> {
if let Some(type_bounds) = type_bounds_opt {
type_bounds.bounds().map(TypeBound::from_ast).collect()
} else {
vec![]
}
}
impl TypeBound {
pub(crate) fn from_ast(node: ast::TypeBound) -> Self {
match node.kind() {
Some(ast::TypeBoundKind::PathType(path_type)) => {
let path = match path_type.path() {
Some(p) => p,
None => return TypeBound::Error,
};
let path = match Path::from_ast(path) {
Some(p) => p,
None => return TypeBound::Error,
};
TypeBound::Path(path)
}
Some(ast::TypeBoundKind::ForType(_)) | Some(ast::TypeBoundKind::Lifetime(_)) | None => {
TypeBound::Error
}
}
}
}

View File

@ -15,7 +15,7 @@ use crate::{
pub use self::{
expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp},
extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind},
extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind},
generated::*,
tokens::*,
traits::*,

View File

@ -399,3 +399,29 @@ impl ast::TraitDef {
self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeBoundKind {
/// Trait
PathType(ast::PathType),
/// for<'a> ...
ForType(ast::ForType),
/// 'a
Lifetime(ast::SyntaxToken),
}
impl ast::TypeBound {
pub fn kind(&self) -> Option<TypeBoundKind> {
let child = self.syntax.first_child_or_token()?;
match child.kind() {
PATH_TYPE => Some(TypeBoundKind::PathType(
ast::PathType::cast(child.into_node().unwrap()).unwrap(),
)),
FOR_TYPE => Some(TypeBoundKind::ForType(
ast::ForType::cast(child.into_node().unwrap()).unwrap(),
)),
LIFETIME => Some(TypeBoundKind::Lifetime(child.into_token().unwrap())),
_ => unreachable!(),
}
}
}