]> git.lizzy.rs Git - rust.git/commitdiff
Fix handling of const patterns
authorFlorian Diebold <florian.diebold@freiheit.com>
Fri, 21 Feb 2020 15:56:34 +0000 (16:56 +0100)
committerFlorian Diebold <florian.diebold@freiheit.com>
Fri, 21 Feb 2020 16:01:19 +0000 (17:01 +0100)
E.g. in `match x { None => ... }`, `None` is a path pattern (resolving to the
option variant), not a binding. To determine this, we need to try to resolve the
name during lowering. This isn't too hard since we already need to resolve names
for macro expansion anyway (though maybe a bit hacky).

Fixes #1618.

crates/ra_hir_def/src/adt.rs
crates/ra_hir_def/src/body/lower.rs
crates/ra_hir_def/src/expr.rs
crates/ra_hir_ty/src/infer/pat.rs
crates/ra_hir_ty/src/tests/patterns.rs

index 985f409e873936f013f58702b26b40beb077a5aa..2bdfc2b8d1229aaec38f2b22d4027512a45bfba4 100644 (file)
@@ -174,6 +174,7 @@ fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId,
     }
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum StructKind {
     Tuple,
     Record,
index b1626fa1160d086ad635d270fbee705455700fd6..b3fb6d452aa623edf28913c2aea9f3a9473ecbf0 100644 (file)
@@ -15,6 +15,7 @@
 use test_utils::tested_by;
 
 use crate::{
+    adt::StructKind,
     body::{Body, BodySourceMap, Expander, PatPtr},
     builtin_type::{BuiltinFloat, BuiltinInt},
     db::DefDatabase,
         ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp,
         MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
     },
+    item_scope::BuiltinShadowMode,
     path::GenericArgs,
     path::Path,
     type_ref::{Mutability, TypeRef},
-    ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, StaticLoc,
-    StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
+    AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
+    StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
 };
 
 pub(super) fn lower(
@@ -571,7 +573,37 @@ fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
                 let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
                 let annotation = BindingAnnotation::new(bp.is_mutable(), bp.is_ref());
                 let subpat = bp.pat().map(|subpat| self.collect_pat(subpat));
-                Pat::Bind { name, mode: annotation, subpat }
+                if annotation == BindingAnnotation::Unannotated && subpat.is_none() {
+                    // This could also be a single-segment path pattern. To
+                    // decide that, we need to try resolving the name.
+                    let (resolved, _) = self.expander.crate_def_map.resolve_path(
+                        self.db,
+                        self.expander.module.local_id,
+                        &name.clone().into(),
+                        BuiltinShadowMode::Other,
+                    );
+                    match resolved.take_values() {
+                        Some(ModuleDefId::ConstId(_)) => Pat::Path(name.into()),
+                        Some(ModuleDefId::EnumVariantId(_)) => {
+                            // this is only really valid for unit variants, but
+                            // shadowing other enum variants with a pattern is
+                            // an error anyway
+                            Pat::Path(name.into())
+                        }
+                        Some(ModuleDefId::AdtId(AdtId::StructId(s)))
+                            if self.db.struct_data(s).variant_data.kind() != StructKind::Record =>
+                        {
+                            // Funnily enough, record structs *can* be shadowed
+                            // by pattern bindings (but unit or tuple structs
+                            // can't).
+                            Pat::Path(name.into())
+                        }
+                        // shadowing statics is an error as well, so we just ignore that case here
+                        _ => Pat::Bind { name, mode: annotation, subpat },
+                    }
+                } else {
+                    Pat::Bind { name, mode: annotation, subpat }
+                }
             }
             ast::Pat::TupleStructPat(p) => {
                 let path = p.path().and_then(|path| self.expander.parse_path(path));
index 9707c55276af77b3bc0bb83451e6af8f73173313..66d0047173cc9d1b250400ed874100fd748cb040 100644 (file)
@@ -48,7 +48,7 @@ pub enum Literal {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Expr {
-    /// This is produced if syntax tree does not have a required expression piece.
+    /// This is produced if the syntax tree does not have a required expression piece.
     Missing,
     Path(Path),
     If {
index a495ecbfeb05667a7c4600446ea5c16c57667b9e..bf8ea192b7ac2d9a5c05737622ec96d65973bac3 100644 (file)
@@ -189,7 +189,9 @@ pub(super) fn infer_pat(
         };
         // use a new type variable if we got Ty::Unknown here
         let ty = self.insert_type_vars_shallow(ty);
-        self.unify(&ty, expected);
+        if !self.unify(&ty, expected) {
+            // FIXME record mismatch, we need to change the type of self.type_mismatches for that
+        }
         let ty = self.resolve_ty_as_possible(ty);
         self.write_pat_ty(pat, ty.clone());
         ty
index e25d6dbc43d46b32f092c401e8f4db7f631a0596..81d00c2afb8bc92d1d6208b01dffe1de0a206a58 100644 (file)
@@ -1,4 +1,4 @@
-use super::infer;
+use super::{infer, infer_with_mismatches};
 use insta::assert_snapshot;
 use test_utils::covers;
 
@@ -236,3 +236,47 @@ fn test(a1: A<u32>, o: Option<u64>) {
     "###
     );
 }
+
+#[test]
+fn infer_const_pattern() {
+    assert_snapshot!(
+        infer_with_mismatches(r#"
+enum Option<T> { None }
+use Option::None;
+struct Foo;
+const Bar: usize = 1;
+
+fn test() {
+    let a: Option<u32> = None;
+    let b: Option<i64> = match a {
+        None => None,
+    };
+    let _: () = match () { Foo => Foo }; // Expected mismatch
+    let _: () = match () { Bar => Bar }; // Expected mismatch
+}
+"#, true),
+        @r###"
+    [74; 75) '1': usize
+    [88; 310) '{     ...atch }': ()
+    [98; 99) 'a': Option<u32>
+    [115; 119) 'None': Option<u32>
+    [129; 130) 'b': Option<i64>
+    [146; 183) 'match ...     }': Option<i64>
+    [152; 153) 'a': Option<u32>
+    [164; 168) 'None': Option<u32>
+    [172; 176) 'None': Option<i64>
+    [193; 194) '_': ()
+    [201; 224) 'match ... Foo }': Foo
+    [207; 209) '()': ()
+    [212; 215) 'Foo': Foo
+    [219; 222) 'Foo': Foo
+    [255; 256) '_': ()
+    [263; 286) 'match ... Bar }': usize
+    [269; 271) '()': ()
+    [274; 277) 'Bar': usize
+    [281; 284) 'Bar': usize
+    [201; 224): expected (), got Foo
+    [263; 286): expected (), got usize
+    "###
+    );
+}