Add initial (flawed) implementation of binding annotations

This commit is contained in:
Marcus Klaas de Vries 2019-01-17 13:08:18 +01:00 committed by Aleksey Kladov
parent 9433a108cf
commit d48d5b8b6c
7 changed files with 97 additions and 5 deletions

View file

@ -88,7 +88,7 @@ impl FnScopes {
fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
match &body[pat] { match &body[pat] {
Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry { Pat::Bind { name, .. } => self.scopes[scope].entries.push(ScopeEntry {
name: name.clone(), name: name.clone(),
pat, pat,
}), }),

View file

@ -329,6 +329,43 @@ impl Expr {
pub struct PatId(RawId); pub struct PatId(RawId);
impl_arena_id!(PatId); impl_arena_id!(PatId);
// copied verbatim from librustc::hir
/// Explicit binding annotations given in the HIR for a binding. Note
/// that this is not the final binding *mode* that we infer after type
/// inference.
#[derive(Clone, PartialEq, Eq, Debug, Copy)]
pub enum BindingAnnotation {
/// No binding annotation given: this means that the final binding mode
/// will depend on whether we have skipped through a `&` reference
/// when matching. For example, the `x` in `Some(x)` will have binding
/// mode `None`; if you do `let Some(x) = &Some(22)`, it will
/// ultimately be inferred to be by-reference.
///
/// Note that implicit reference skipping is not implemented yet (#42640).
Unannotated,
/// Annotated with `mut x` -- could be either ref or not, similar to `None`.
Mutable,
/// Annotated as `ref`, like `ref x`
Ref,
/// Annotated as `ref mut x`.
RefMut,
}
impl BindingAnnotation {
fn new(is_mutable: bool, is_ref: bool) -> Self {
match (is_mutable, is_ref) {
(true, true) => BindingAnnotation::RefMut,
(false, true) => BindingAnnotation::Ref,
(true, false) => BindingAnnotation::Mutable,
(false, false) => BindingAnnotation::Unannotated,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct FieldPat { pub struct FieldPat {
pub(crate) name: Name, pub(crate) name: Name,
@ -359,7 +396,9 @@ pub enum Pat {
Path(Path), Path(Path),
Lit(ExprId), Lit(ExprId),
Bind { Bind {
mode: BindingAnnotation,
name: Name, name: Name,
sub_pat: Option<PatId>,
}, },
TupleStruct { TupleStruct {
path: Option<Path>, path: Option<Path>,
@ -793,7 +832,13 @@ impl ExprCollector {
.name() .name()
.map(|nr| nr.as_name()) .map(|nr| nr.as_name())
.unwrap_or_else(Name::missing); .unwrap_or_else(Name::missing);
Pat::Bind { name } let annotation = BindingAnnotation::new(bp.is_mutable(), bp.is_ref());
let sub_pat = bp.pat().map(|subpat| self.collect_pat(subpat));
Pat::Bind {
name,
mode: annotation,
sub_pat,
}
} }
ast::PatKind::TupleStructPat(p) => { ast::PatKind::TupleStructPat(p) => {
let path = p.path().and_then(Path::from_ast); let path = p.path().and_then(Path::from_ast);
@ -882,6 +927,8 @@ pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping {
let param = collector.alloc_pat( let param = collector.alloc_pat(
Pat::Bind { Pat::Bind {
name: Name::self_param(), name: Name::self_param(),
mode: BindingAnnotation::Unannotated,
sub_pat: None,
}, },
self_param, self_param,
); );

View file

@ -37,7 +37,7 @@ use crate::{
db::HirDatabase, db::HirDatabase,
type_ref::{TypeRef, Mutability}, type_ref::{TypeRef, Mutability},
name::KnownName, name::KnownName,
expr::{Body, Expr, MatchArm, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, expr::{Body, Expr, BindingAnnotation, MatchArm, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat},
}; };
/// The ID of a type variable. /// The ID of a type variable.
@ -985,6 +985,30 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
path: ref p, path: ref p,
args: ref fields, args: ref fields,
} => self.infer_struct(p.as_ref(), fields), } => self.infer_struct(p.as_ref(), fields),
Pat::Path(path) => {
// is this right?
self.module
.resolve_path(self.db, &path)
.take_values()
.map_or(Ty::Unknown, |resolved| self.db.type_for_def(resolved))
}
Pat::Bind {
mode,
name: _name,
sub_pat,
} => {
let subty = if let Some(subpat) = sub_pat {
self.infer_pat(*subpat, expected)
} else {
Ty::Unknown
};
match mode {
BindingAnnotation::Ref => Ty::Ref(subty.into(), Mutability::Shared),
BindingAnnotation::RefMut => Ty::Ref(subty.into(), Mutability::Mut),
BindingAnnotation::Mutable | BindingAnnotation::Unannotated => subty,
}
}
_ => Ty::Unknown, _ => Ty::Unknown,
}; };
// use a new type variable if we got Ty::Unknown here // use a new type variable if we got Ty::Unknown here

View file

@ -377,6 +377,10 @@ fn test(x: &i32) {
} }
let lambda = |a: u64, b, c: i32| { a + b; c }; let lambda = |a: u64, b, c: i32| { a + b; c };
let ref ref_to_x = x;
let mut mut_x = x;
let ref mut mut_ref_to_x = x;
} }
"#, "#,
"pattern.txt", "pattern.txt",

View file

@ -713,6 +713,16 @@ impl FieldPatList {
} }
} }
impl BindPat {
pub fn is_mutable(&self) -> bool {
self.syntax().children().any(|n| n.kind() == MUT_KW)
}
pub fn is_ref(&self) -> bool {
self.syntax().children().any(|n| n.kind() == REF_KW)
}
}
#[test] #[test]
fn test_doc_comment_of_items() { fn test_doc_comment_of_items() {
let file = SourceFile::parse( let file = SourceFile::parse(

View file

@ -180,7 +180,11 @@ impl AstNode for BindPat {
impl ast::NameOwner for BindPat {} impl ast::NameOwner for BindPat {}
impl BindPat {} impl BindPat {
pub fn pat(&self) -> Option<&Pat> {
super::child_opt(self)
}
}
// Block // Block
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]

View file

@ -488,7 +488,10 @@ Grammar(
), ),
"RefPat": ( options: [ "Pat" ]), "RefPat": ( options: [ "Pat" ]),
"BindPat": ( traits: ["NameOwner"] ), "BindPat": (
options: [ "Pat" ],
traits: ["NameOwner"]
),
"PlaceholderPat": (), "PlaceholderPat": (),
"PathPat": ( options: [ "Path" ] ), "PathPat": ( options: [ "Path" ] ),
"StructPat": ( options: ["FieldPatList", "Path"] ), "StructPat": ( options: ["FieldPatList", "Path"] ),