From 7adb681b1f8dbdc0205efbe22698c28ab9da6378 Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Thu, 16 Apr 2020 17:08:24 -0700 Subject: missing match arm diagnostic support enum record type --- crates/ra_hir_ty/src/_match.rs | 336 ++++++++++++++++++++++++++++++++++++---- crates/ra_hir_ty/src/test_db.rs | 36 +++-- 2 files changed, 331 insertions(+), 41 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 688026a04..779e78574 100644 --- a/crates/ra_hir_ty/src/_match.rs +++ b/crates/ra_hir_ty/src/_match.rs @@ -235,10 +235,19 @@ impl From for PatIdOrWild { } } +impl From<&PatId> for PatIdOrWild { + fn from(pat_id: &PatId) -> Self { + Self::PatId(*pat_id) + } +} + #[derive(Debug, Clone, Copy, PartialEq)] pub enum MatchCheckErr { NotImplemented, MalformedMatchArm, + /// Used when type inference cannot resolve the type of + /// a pattern or expression. + Unknown, } /// The return type of `is_useful` is either an indication of usefulness @@ -290,10 +299,14 @@ impl PatStack { Self::from_slice(&self.0[1..]) } - fn replace_head_with + Copy>(&self, pat_ids: &[T]) -> PatStack { + fn replace_head_with(&self, pats: I) -> PatStack + where + I: Iterator, + T: Into, + { let mut patterns: PatStackInner = smallvec![]; - for pat in pat_ids { - patterns.push((*pat).into()); + for pat in pats { + patterns.push(pat.into()); } for pat in &self.0[1..] { patterns.push(*pat); @@ -330,7 +343,7 @@ impl PatStack { return Err(MatchCheckErr::NotImplemented); } - Some(self.replace_head_with(pat_ids)) + Some(self.replace_head_with(pat_ids.iter())) } (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { match cx.body.exprs[lit_expr] { @@ -382,7 +395,7 @@ impl PatStack { new_patterns.push((*pat_id).into()); } - Some(self.replace_head_with(&new_patterns)) + Some(self.replace_head_with(new_patterns.into_iter())) } else { return Err(MatchCheckErr::MalformedMatchArm); } @@ -390,13 +403,41 @@ impl PatStack { // If there is no ellipsis in the tuple pattern, the number // of patterns must equal the constructor arity. if pat_ids.len() == constructor_arity { - Some(self.replace_head_with(pat_ids)) + Some(self.replace_head_with(pat_ids.into_iter())) } else { return Err(MatchCheckErr::MalformedMatchArm); } } } } + (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { + let pat_id = self.head().as_id().expect("we know this isn't a wild"); + if !enum_variant_matches(cx, pat_id, *e) { + None + } else { + match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { + VariantData::Record(struct_field_arena) => { + // Here we treat any missing fields in the record as the wild pattern, as + // if the record has ellipsis. We want to do this here even if the + // record does not contain ellipsis, because it allows us to continue + // enforcing exhaustiveness for the rest of the match statement. + // + // Creating the diagnostic for the missing field in the pattern + // should be done in a different diagnostic. + let patterns = struct_field_arena.iter().map(|(_, struct_field)| { + arg_patterns + .iter() + .find(|pat| pat.name == struct_field.name) + .map(|pat| PatIdOrWild::from(pat.pat)) + .unwrap_or(PatIdOrWild::Wild) + }); + + Some(self.replace_head_with(patterns)) + } + _ => return Err(MatchCheckErr::Unknown), + } + } + } (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), (_, _) => return Err(MatchCheckErr::NotImplemented), }; @@ -655,8 +696,8 @@ impl Constructor { Constructor::Enum(e) => { match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { VariantData::Tuple(struct_field_data) => struct_field_data.len(), + VariantData::Record(struct_field_data) => struct_field_data.len(), VariantData::Unit => 0, - _ => return Err(MatchCheckErr::NotImplemented), } } }; @@ -695,10 +736,10 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult Some(Constructor::Bool(val)), _ => return Err(MatchCheckErr::NotImplemented), }, - Pat::TupleStruct { .. } | Pat::Path(_) => { + Pat::TupleStruct { .. } | Pat::Path(_) | Pat::Record { .. } => { let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); let variant_id = - cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::NotImplemented)?; + cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::Unknown)?; match variant_id { VariantId::EnumVariantId(enum_variant_id) => { Some(Constructor::Enum(enum_variant_id)) @@ -759,20 +800,22 @@ mod tests { pub(super) use insta::assert_snapshot; pub(super) use ra_db::fixture::WithFixture; - pub(super) use crate::test_db::TestDB; + pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; pub(super) fn check_diagnostic_message(content: &str) -> String { - TestDB::with_single_file(content).0.diagnostics().0 + TestDB::with_single_file(content).0.diagnostic::().0 } pub(super) fn check_diagnostic(content: &str) { - let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; + let diagnostic_count = + TestDB::with_single_file(content).0.diagnostic::().1; assert_eq!(1, diagnostic_count, "no diagnostic reported"); } pub(super) fn check_no_diagnostic(content: &str) { - let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; + let diagnostic_count = + TestDB::with_single_file(content).0.diagnostic::().1; assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); } @@ -1531,6 +1574,236 @@ mod tests { check_no_diagnostic(content); } + #[test] + fn enum_record_no_arms() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_missing_arms() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: true } => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: true } => (), + Either::A { foo: false } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_missing_field_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { } => (), + Either::B => (), + } + } + "; + + // When `Either::A` is missing a struct member, we don't want + // to fire the missing match arm diagnostic. This should fire + // some other diagnostic. + check_no_diagnostic(content); + } + + #[test] + fn enum_record_missing_field_missing_match_arm() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { } => (), + } + } + "; + + // Even though `Either::A` is missing fields, we still want to fire + // the missing arm diagnostic here, since we know `Either::B` is missing. + check_diagnostic(content); + } + + #[test] + fn enum_record_no_diagnostic_wild() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: _ } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_fields_out_of_order_missing_arm() { + let content = r" + enum Either { + A { foo: bool, bar: () }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { bar: (), foo: false } => (), + Either::A { foo: true, bar: () } => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_fields_out_of_order_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool, bar: () }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { bar: (), foo: false } => (), + Either::A { foo: true, bar: () } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_missing_arm() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + match Either::B { + Either::A { foo: true, .. } => (), + Either::B => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: true, .. } => (), + Either::A { foo: false, .. } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_all_fields_missing_arm() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { .. } => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_all_fields_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { .. } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + #[test] fn enum_tuple_partial_ellipsis_no_diagnostic() { let content = r" @@ -1688,25 +1961,6 @@ mod false_negatives { check_no_diagnostic(content); } - #[test] - fn enum_record() { - let content = r" - enum Either { - A { foo: u32 }, - B, - } - fn test_fn() { - match Either::B { - Either::A { foo: 5 } => (), - } - } - "; - - // This is a false negative. - // We don't currently handle enum record types. - check_no_diagnostic(content); - } - #[test] fn internal_or() { let content = r" @@ -1796,4 +2050,22 @@ mod false_negatives { // We don't currently handle tuple patterns with ellipsis. check_no_diagnostic(content); } + + #[test] + fn struct_missing_arm() { + let content = r" + struct Foo { + a: bool, + } + fn test_fn(f: Foo) { + match f { + Foo { a: true } => {}, + } + } + "; + + // This is a false negative. + // We don't currently handle structs. + check_no_diagnostic(content); + } } diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 3a4d58bf9..8498d3d96 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -12,7 +12,7 @@ use ra_db::{ }; use stdx::format_to; -use crate::{db::HirDatabase, expr::ExprValidator}; +use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; #[salsa::database( ra_db::SourceDatabaseExtStorage, @@ -104,10 +104,7 @@ impl TestDB { panic!("Can't find module for file") } - // FIXME: don't duplicate this - pub fn diagnostics(&self) -> (String, u32) { - let mut buf = String::new(); - let mut count = 0; + fn diag(&self, mut cb: F) { let crate_graph = self.crate_graph(); for krate in crate_graph.iter() { let crate_def_map = self.crate_def_map(krate); @@ -132,15 +129,36 @@ impl TestDB { for f in fns { let infer = self.infer(f.into()); - let mut sink = DiagnosticSink::new(|d| { - format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); - count += 1; - }); + let mut sink = DiagnosticSink::new(&mut cb); infer.add_diagnostics(self, f, &mut sink); let mut validator = ExprValidator::new(f, infer, &mut sink); validator.validate_body(self); } } + } + + pub fn diagnostics(&self) -> (String, u32) { + let mut buf = String::new(); + let mut count = 0; + self.diag(|d| { + format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); + count += 1; + }); + (buf, count) + } + + /// Like `diagnostics`, but filtered for a single diagnostic. + pub fn diagnostic(&self) -> (String, u32) { + let mut buf = String::new(); + let mut count = 0; + self.diag(|d| { + // We want to filter diagnostics by the particular one we are testing for, to + // avoid surprising results in tests. + if d.downcast_ref::().is_some() { + format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); + count += 1; + }; + }); (buf, count) } } -- cgit v1.2.3 From fa2ea8f494d8434da705dc0e0f047f3bd7503af9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 18 Apr 2020 22:05:06 +0200 Subject: Fix goto definition for record patterns --- crates/ra_hir_ty/src/infer.rs | 4 ++++ crates/ra_hir_ty/src/infer/pat.rs | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index b6d9b3438..dfb6a435f 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -127,6 +127,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each field in record literal, records the field it resolves to. record_field_resolutions: FxHashMap, + record_field_pat_resolutions: FxHashMap, /// For each struct literal, records the variant it resolves to. variant_resolutions: FxHashMap, /// For each associated item record what it resolves to @@ -147,6 +148,9 @@ impl InferenceResult { pub fn record_field_resolution(&self, expr: ExprId) -> Option { self.record_field_resolutions.get(&expr).copied() } + pub fn record_field_pat_resolution(&self, pat: PatId) -> Option { + self.record_field_pat_resolutions.get(&pat).copied() + } pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option { self.variant_resolutions.get(&id.into()).copied() } diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 8ec4d4ace..7c2ad4384 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs @@ -7,6 +7,7 @@ use hir_def::{ expr::{BindingAnnotation, Pat, PatId, RecordFieldPat}, path::Path, type_ref::Mutability, + StructFieldId, }; use hir_expand::name::Name; use test_utils::tested_by; @@ -67,6 +68,11 @@ impl<'a> InferenceContext<'a> { let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); for subpat in subpats { let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); + if let Some(local_id) = matching_field { + let field_def = StructFieldId { parent: def.unwrap(), local_id }; + self.result.record_field_pat_resolutions.insert(subpat.pat, field_def); + } + let expected_ty = matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs)); let expected_ty = self.normalize_associated_types_in(expected_ty); -- cgit v1.2.3 From b79fd82559642f4fe504c0c382b86d57c666be1d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 18 Apr 2020 22:16:04 +0200 Subject: Correctly infer types in guard expressions The root cause was that we forgot to add bindings from the arm to the guard expression closes #3980 --- crates/ra_hir_ty/src/tests/patterns.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index 07cbc521a..6ea51d5d3 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs @@ -455,3 +455,29 @@ fn test() { "### ); } + +#[test] +fn infer_guard() { + assert_snapshot!( + infer(r#" +struct S; +impl S { fn foo(&self) -> bool { false } } + +fn main() { + match S { + s if s.foo() => (), + } +} + "#), @" + [28; 32) 'self': &S + [42; 51) '{ false }': bool + [44; 49) 'false': bool + [65; 116) '{ ... } }': () + [71; 114) 'match ... }': () + [77; 78) 'S': S + [89; 90) 's': S + [94; 95) 's': S + [94; 101) 's.foo()': bool + [105; 107) '()': () + ") +} -- cgit v1.2.3 From 0be68a482581861f4218e0a759e2da71ee19fce6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 18 Apr 2020 13:36:35 +0200 Subject: Update Chalk, and cache Chalk env elaboration through a query This should fix some of the worst performance problems. --- crates/ra_hir_ty/Cargo.toml | 6 +- crates/ra_hir_ty/src/db.rs | 7 ++ crates/ra_hir_ty/src/traits.rs | 2 +- crates/ra_hir_ty/src/traits/chalk.rs | 145 ++++++++++++++++++++++++++----- crates/ra_hir_ty/src/traits/chalk/tls.rs | 33 ++++++- 5 files changed, 164 insertions(+), 29 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 177bdbcb0..04d3cd6a2 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml @@ -27,9 +27,9 @@ test_utils = { path = "../test_utils" } scoped-tls = "1" -chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } -chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } -chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } +chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } +chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } +chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } [dev-dependencies] insta = "0.16.0" diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index 33da16b48..9e5dfeab3 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs @@ -107,6 +107,13 @@ pub trait HirDatabase: DefDatabase + Upcast { krate: CrateId, goal: crate::Canonical>, ) -> Option; + + #[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)] + fn program_clauses_for_chalk_env( + &self, + krate: CrateId, + env: chalk_ir::Environment, + ) -> chalk_ir::ProgramClauses; } fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc { diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 05791a848..6bc6d474c 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -225,7 +225,7 @@ fn solution_from_chalk( None => unimplemented!(), }) .collect(); - let result = Canonical { value, num_vars: subst.binders.len() }; + let result = Canonical { value, num_vars: subst.binders.len(&Interner) }; SolutionVariables(result) }; match solution { diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index e00a82db2..1ccb7c3b4 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -4,8 +4,8 @@ use std::{fmt, sync::Arc}; use log::debug; use chalk_ir::{ - cast::Cast, fold::shift::Shift, Goal, GoalData, Parameter, PlaceholderIndex, TypeName, - UniverseIndex, + cast::Cast, fold::shift::Shift, interner::HasInterner, Goal, GoalData, Parameter, + PlaceholderIndex, TypeName, UniverseIndex, }; use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; @@ -33,8 +33,10 @@ impl chalk_ir::interner::Interner for Interner { type InternedGoals = Vec>; type InternedSubstitution = Vec>; type InternedProgramClause = chalk_ir::ProgramClauseData; - type InternedProgramClauses = Vec>; + type InternedProgramClauses = Arc<[chalk_ir::ProgramClause]>; type InternedQuantifiedWhereClauses = Vec>; + type InternedParameterKinds = Vec>; + type InternedCanonicalVarKinds = Vec>; type Identifier = TypeAliasId; type DefId = InternId; @@ -60,6 +62,27 @@ impl chalk_ir::interner::Interner for Interner { tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt))) } + fn debug_projection_ty( + proj: &chalk_ir::ProjectionTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) + } + + fn debug_opaque_ty( + opaque_ty: &chalk_ir::OpaqueTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt))) + } + + fn debug_opaque_ty_id( + opaque_ty_id: chalk_ir::OpaqueTyId, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt))) + } + fn debug_ty(ty: &chalk_ir::Ty, fmt: &mut fmt::Formatter<'_>) -> Option { tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt))) } @@ -202,15 +225,15 @@ impl chalk_ir::interner::Interner for Interner { fn intern_program_clauses( &self, data: impl IntoIterator>, - ) -> Vec> { + ) -> Arc<[chalk_ir::ProgramClause]> { data.into_iter().collect() } fn program_clauses_data<'a>( &self, - clauses: &'a Vec>, + clauses: &'a Arc<[chalk_ir::ProgramClause]>, ) -> &'a [chalk_ir::ProgramClause] { - clauses + &clauses } fn intern_quantified_where_clauses( @@ -226,6 +249,34 @@ impl chalk_ir::interner::Interner for Interner { ) -> &'a [chalk_ir::QuantifiedWhereClause] { clauses } + + fn intern_parameter_kinds( + &self, + data: impl IntoIterator>, + ) -> Self::InternedParameterKinds { + data.into_iter().collect() + } + + fn parameter_kinds_data<'a>( + &self, + parameter_kinds: &'a Self::InternedParameterKinds, + ) -> &'a [chalk_ir::ParameterKind<()>] { + ¶meter_kinds + } + + fn intern_canonical_var_kinds( + &self, + data: impl IntoIterator>, + ) -> Self::InternedCanonicalVarKinds { + data.into_iter().collect() + } + + fn canonical_var_kinds_data<'a>( + &self, + canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, + ) -> &'a [chalk_ir::ParameterKind] { + &canonical_var_kinds + } } impl chalk_ir::interner::HasInterner for Interner { @@ -268,9 +319,12 @@ impl ToChalk for Ty { Ty::Projection(proj_ty) => { let associated_ty_id = proj_ty.associated_ty.to_chalk(db); let substitution = proj_ty.parameters.to_chalk(db); - chalk_ir::AliasTy { associated_ty_id, substitution } - .cast(&Interner) - .intern(&Interner) + chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }) + .cast(&Interner) + .intern(&Interner) } Ty::Placeholder(id) => { let interned_id = db.intern_type_param_id(id); @@ -314,16 +368,17 @@ impl ToChalk for Ty { ); Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) } - chalk_ir::TyData::Alias(proj) => { + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => { let associated_ty = from_chalk(db, proj.associated_ty_id); let parameters = from_chalk(db, proj.substitution); Ty::Projection(ProjectionTy { associated_ty, parameters }) } + chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), chalk_ir::TyData::Function(_) => unimplemented!(), chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx), chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown, chalk_ir::TyData::Dyn(where_clauses) => { - assert_eq!(where_clauses.bounds.binders.len(), 1); + assert_eq!(where_clauses.bounds.binders.len(&Interner), 1); let predicates = where_clauses .bounds .skip_binders() @@ -404,6 +459,7 @@ impl ToChalk for TypeCtor { match type_name { TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), + TypeName::OpaqueType(_) => unreachable!(), TypeName::Error => { // this should not be reached, since we don't represent TypeName::Error with TypeCtor unreachable!() @@ -460,7 +516,8 @@ impl ToChalk for GenericPredicate { } GenericPredicate::Projection(projection_pred) => { let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner); - let alias = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner); + let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner); + let alias = chalk_ir::AliasTy::Projection(projection); make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0) } GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"), @@ -481,7 +538,13 @@ impl ToChalk for GenericPredicate { GenericPredicate::Implemented(from_chalk(db, tr)) } chalk_ir::WhereClause::AliasEq(projection_eq) => { - let projection_ty = from_chalk(db, projection_eq.alias); + let projection_ty = from_chalk( + db, + match projection_eq.alias { + chalk_ir::AliasTy::Projection(p) => p, + _ => unimplemented!(), + }, + ); let ty = from_chalk(db, projection_eq.ty); GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty }) } @@ -490,10 +553,10 @@ impl ToChalk for GenericPredicate { } impl ToChalk for ProjectionTy { - type Chalk = chalk_ir::AliasTy; + type Chalk = chalk_ir::ProjectionTy; - fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasTy { - chalk_ir::AliasTy { + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy { + chalk_ir::ProjectionTy { associated_ty_id: self.associated_ty.to_chalk(db), substitution: self.parameters.to_chalk(db), } @@ -501,7 +564,7 @@ impl ToChalk for ProjectionTy { fn from_chalk( db: &dyn HirDatabase, - projection_ty: chalk_ir::AliasTy, + projection_ty: chalk_ir::ProjectionTy, ) -> ProjectionTy { ProjectionTy { associated_ty: from_chalk(db, projection_ty.associated_ty_id), @@ -514,7 +577,10 @@ impl ToChalk for super::ProjectionPredicate { type Chalk = chalk_ir::AliasEq; fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq { - chalk_ir::AliasEq { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } + chalk_ir::AliasEq { + alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)), + ty: self.ty.to_chalk(db), + } } fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq) -> Self { @@ -540,17 +606,24 @@ impl ToChalk for Obligation { impl ToChalk for Canonical where T: ToChalk, + T::Chalk: HasInterner, { type Chalk = chalk_ir::Canonical; fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical { let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT); let value = self.value.to_chalk(db); - chalk_ir::Canonical { value, binders: vec![parameter; self.num_vars] } + chalk_ir::Canonical { + value, + binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]), + } } fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical) -> Canonical { - Canonical { num_vars: canonical.binders.len(), value: from_chalk(db, canonical.value) } + Canonical { + num_vars: canonical.binders.len(&Interner), + value: from_chalk(db, canonical.value), + } } } @@ -649,9 +722,15 @@ impl ToChalk for builtin::BuiltinImplAssocTyValueData { } } -fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders { +fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders +where + T: HasInterner, +{ chalk_ir::Binders::new( - std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars).collect(), + chalk_ir::ParameterKinds::from( + &Interner, + std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars), + ), value, ) } @@ -799,6 +878,28 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { // FIXME tell Chalk about well-known traits (here and in trait_datum) None } + + fn program_clauses_for_env( + &self, + environment: &chalk_ir::Environment, + ) -> chalk_ir::ProgramClauses { + self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) + } + + fn opaque_ty_data( + &self, + _id: chalk_ir::OpaqueTyId, + ) -> Arc> { + unimplemented!() + } +} + +pub(crate) fn program_clauses_for_chalk_env_query( + db: &dyn HirDatabase, + krate: CrateId, + environment: chalk_ir::Environment, +) -> chalk_ir::ProgramClauses { + chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment) } pub(crate) fn associated_ty_data_query( diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs index fa8e4d1ad..4867cb17e 100644 --- a/crates/ra_hir_ty/src/traits/chalk/tls.rs +++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs @@ -121,19 +121,38 @@ impl DebugContext<'_> { write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) } + pub fn debug_opaque_ty_id( + &self, + opaque_ty_id: chalk_ir::OpaqueTyId, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish() + } + pub fn debug_alias( &self, - alias: &AliasTy, + alias_ty: &AliasTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + match alias_ty { + AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt), + AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt), + } + } + + pub fn debug_projection_ty( + &self, + projection_ty: &chalk_ir::ProjectionTy, fmt: &mut fmt::Formatter<'_>, ) -> Result<(), fmt::Error> { - let type_alias: TypeAliasId = from_chalk(self.0, alias.associated_ty_id); + let type_alias: TypeAliasId = from_chalk(self.0, projection_ty.associated_ty_id); let type_alias_data = self.0.type_alias_data(type_alias); let trait_ = match type_alias.lookup(self.0.upcast()).container { AssocContainerId::TraitId(t) => t, _ => panic!("associated type not in trait"), }; let trait_data = self.0.trait_data(trait_); - let params = alias.substitution.parameters(&Interner); + let params = projection_ty.substitution.parameters(&Interner); write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?; if params.len() > 1 { write!( @@ -145,6 +164,14 @@ impl DebugContext<'_> { write!(fmt, ">::{}", type_alias_data.name) } + pub fn debug_opaque_ty( + &self, + opaque_ty: &chalk_ir::OpaqueTy, + fmt: &mut fmt::Formatter<'_>, + ) -> Result<(), fmt::Error> { + write!(fmt, "{:?}", opaque_ty.opaque_ty_id) + } + pub fn debug_ty( &self, ty: &chalk_ir::Ty, -- cgit v1.2.3