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) {
match &body[pat] {
Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry {
Pat::Bind { name, .. } => self.scopes[scope].entries.push(ScopeEntry {
name: name.clone(),
pat,
}),

View File

@ -329,6 +329,43 @@ impl Expr {
pub struct PatId(RawId);
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)]
pub struct FieldPat {
pub(crate) name: Name,
@ -359,7 +396,9 @@ pub enum Pat {
Path(Path),
Lit(ExprId),
Bind {
mode: BindingAnnotation,
name: Name,
sub_pat: Option<PatId>,
},
TupleStruct {
path: Option<Path>,
@ -793,7 +832,13 @@ impl ExprCollector {
.name()
.map(|nr| nr.as_name())
.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) => {
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(
Pat::Bind {
name: Name::self_param(),
mode: BindingAnnotation::Unannotated,
sub_pat: None,
},
self_param,
);

View File

@ -37,7 +37,7 @@ use crate::{
db::HirDatabase,
type_ref::{TypeRef, Mutability},
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.
@ -985,6 +985,30 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
path: ref p,
args: 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,
};
// 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 ref ref_to_x = x;
let mut mut_x = x;
let ref mut mut_ref_to_x = x;
}
"#,
"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]
fn test_doc_comment_of_items() {
let file = SourceFile::parse(

View File

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

View File

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