aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/Cargo.toml2
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs31
-rw-r--r--crates/ra_hir_ty/src/display.rs26
-rw-r--r--crates/ra_hir_ty/src/expr.rs114
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs2
-rw-r--r--crates/ra_hir_ty/src/tests.rs106
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs31
-rw-r--r--crates/ra_hir_ty/src/traits.rs13
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs81
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs231
10 files changed, 543 insertions, 94 deletions
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 9a4a7aa6f..59efc1c31 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -24,6 +24,8 @@ ra_prof = { path = "../ra_prof" }
24ra_syntax = { path = "../ra_syntax" } 24ra_syntax = { path = "../ra_syntax" }
25test_utils = { path = "../test_utils" } 25test_utils = { path = "../test_utils" }
26 26
27scoped-tls = "1"
28
27chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } 29chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" }
28chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } 30chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" }
29chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } 31chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" }
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 8cbce1168..927896d6f 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -21,7 +21,7 @@ impl Diagnostic for NoSuchField {
21 } 21 }
22 22
23 fn source(&self) -> InFile<SyntaxNodePtr> { 23 fn source(&self) -> InFile<SyntaxNodePtr> {
24 InFile { file_id: self.file, value: self.field.into() } 24 InFile { file_id: self.file, value: self.field.clone().into() }
25 } 25 }
26 26
27 fn as_any(&self) -> &(dyn Any + Send + 'static) { 27 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -45,7 +45,7 @@ impl Diagnostic for MissingFields {
45 buf 45 buf
46 } 46 }
47 fn source(&self) -> InFile<SyntaxNodePtr> { 47 fn source(&self) -> InFile<SyntaxNodePtr> {
48 InFile { file_id: self.file, value: self.field_list.into() } 48 InFile { file_id: self.file, value: self.field_list.clone().into() }
49 } 49 }
50 fn as_any(&self) -> &(dyn Any + Send + 'static) { 50 fn as_any(&self) -> &(dyn Any + Send + 'static) {
51 self 51 self
@@ -63,6 +63,29 @@ impl AstDiagnostic for MissingFields {
63} 63}
64 64
65#[derive(Debug)] 65#[derive(Debug)]
66pub struct MissingPatFields {
67 pub file: HirFileId,
68 pub field_list: AstPtr<ast::RecordFieldPatList>,
69 pub missed_fields: Vec<Name>,
70}
71
72impl Diagnostic for MissingPatFields {
73 fn message(&self) -> String {
74 let mut buf = String::from("Missing structure fields:\n");
75 for field in &self.missed_fields {
76 format_to!(buf, "- {}", field);
77 }
78 buf
79 }
80 fn source(&self) -> InFile<SyntaxNodePtr> {
81 InFile { file_id: self.file, value: self.field_list.clone().into() }
82 }
83 fn as_any(&self) -> &(dyn Any + Send + 'static) {
84 self
85 }
86}
87
88#[derive(Debug)]
66pub struct MissingMatchArms { 89pub struct MissingMatchArms {
67 pub file: HirFileId, 90 pub file: HirFileId,
68 pub match_expr: AstPtr<ast::Expr>, 91 pub match_expr: AstPtr<ast::Expr>,
@@ -74,7 +97,7 @@ impl Diagnostic for MissingMatchArms {
74 String::from("Missing match arm") 97 String::from("Missing match arm")
75 } 98 }
76 fn source(&self) -> InFile<SyntaxNodePtr> { 99 fn source(&self) -> InFile<SyntaxNodePtr> {
77 InFile { file_id: self.file, value: self.match_expr.into() } 100 InFile { file_id: self.file, value: self.match_expr.clone().into() }
78 } 101 }
79 fn as_any(&self) -> &(dyn Any + Send + 'static) { 102 fn as_any(&self) -> &(dyn Any + Send + 'static) {
80 self 103 self
@@ -92,7 +115,7 @@ impl Diagnostic for MissingOkInTailExpr {
92 "wrap return expression in Ok".to_string() 115 "wrap return expression in Ok".to_string()
93 } 116 }
94 fn source(&self) -> InFile<SyntaxNodePtr> { 117 fn source(&self) -> InFile<SyntaxNodePtr> {
95 InFile { file_id: self.file, value: self.expr.into() } 118 InFile { file_id: self.file, value: self.expr.clone().into() }
96 } 119 }
97 fn as_any(&self) -> &(dyn Any + Send + 'static) { 120 fn as_any(&self) -> &(dyn Any + Send + 'static) {
98 self 121 self
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index 0e9313aa1..d03bbd5a7 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -247,19 +247,21 @@ impl HirDisplay for ApplicationTy {
247 } 247 }
248 } 248 }
249 TypeCtor::Closure { .. } => { 249 TypeCtor::Closure { .. } => {
250 let sig = self.parameters[0] 250 let sig = self.parameters[0].callable_sig(f.db);
251 .callable_sig(f.db) 251 if let Some(sig) = sig {
252 .expect("first closure parameter should contain signature"); 252 if sig.params().is_empty() {
253 if sig.params().is_empty() { 253 write!(f, "||")?;
254 write!(f, "||")?; 254 } else if f.omit_verbose_types() {
255 } else if f.omit_verbose_types() { 255 write!(f, "|{}|", TYPE_HINT_TRUNCATION)?;
256 write!(f, "|{}|", TYPE_HINT_TRUNCATION)?; 256 } else {
257 write!(f, "|")?;
258 f.write_joined(sig.params(), ", ")?;
259 write!(f, "|")?;
260 };
261 write!(f, " -> {}", sig.ret().display(f.db))?;
257 } else { 262 } else {
258 write!(f, "|")?; 263 write!(f, "{{closure}}")?;
259 f.write_joined(sig.params(), ", ")?; 264 }
260 write!(f, "|")?;
261 };
262 write!(f, " -> {}", sig.ret().display(f.db))?;
263 } 265 }
264 } 266 }
265 Ok(()) 267 Ok(())
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index e45e9ea14..827b687de 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -9,7 +9,7 @@ use rustc_hash::FxHashSet;
9 9
10use crate::{ 10use crate::{
11 db::HirDatabase, 11 db::HirDatabase,
12 diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr}, 12 diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields},
13 utils::variant_data, 13 utils::variant_data,
14 ApplicationTy, InferenceResult, Ty, TypeCtor, 14 ApplicationTy, InferenceResult, Ty, TypeCtor,
15 _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, 15 _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
@@ -49,39 +49,97 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
49 if let Some((variant_def, missed_fields, true)) = 49 if let Some((variant_def, missed_fields, true)) =
50 record_literal_missing_fields(db, &self.infer, id, expr) 50 record_literal_missing_fields(db, &self.infer, id, expr)
51 { 51 {
52 // XXX: only look at source_map if we do have missing fields 52 self.create_record_literal_missing_fields_diagnostic(
53 let (_, source_map) = db.body_with_source_map(self.func.into()); 53 id,
54 54 db,
55 if let Ok(source_ptr) = source_map.expr_syntax(id) { 55 variant_def,
56 if let Some(expr) = source_ptr.value.left() { 56 missed_fields,
57 let root = source_ptr.file_syntax(db.upcast()); 57 );
58 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
59 if let Some(field_list) = record_lit.record_field_list() {
60 let variant_data = variant_data(db.upcast(), variant_def);
61 let missed_fields = missed_fields
62 .into_iter()
63 .map(|idx| variant_data.fields()[idx].name.clone())
64 .collect();
65 self.sink.push(MissingFields {
66 file: source_ptr.file_id,
67 field_list: AstPtr::new(&field_list),
68 missed_fields,
69 })
70 }
71 }
72 }
73 }
74 } 58 }
75 if let Expr::Match { expr, arms } = expr { 59 if let Expr::Match { expr, arms } = expr {
76 self.validate_match(id, *expr, arms, db, self.infer.clone()); 60 self.validate_match(id, *expr, arms, db, self.infer.clone());
77 } 61 }
78 } 62 }
63 for (id, pat) in body.pats.iter() {
64 if let Some((variant_def, missed_fields, true)) =
65 record_pattern_missing_fields(db, &self.infer, id, pat)
66 {
67 self.create_record_pattern_missing_fields_diagnostic(
68 id,
69 db,
70 variant_def,
71 missed_fields,
72 );
73 }
74 }
79 let body_expr = &body[body.body_expr]; 75 let body_expr = &body[body.body_expr];
80 if let Expr::Block { tail: Some(t), .. } = body_expr { 76 if let Expr::Block { tail: Some(t), .. } = body_expr {
81 self.validate_results_in_tail_expr(body.body_expr, *t, db); 77 self.validate_results_in_tail_expr(body.body_expr, *t, db);
82 } 78 }
83 } 79 }
84 80
81 fn create_record_literal_missing_fields_diagnostic(
82 &mut self,
83 id: ExprId,
84 db: &dyn HirDatabase,
85 variant_def: VariantId,
86 missed_fields: Vec<LocalStructFieldId>,
87 ) {
88 // XXX: only look at source_map if we do have missing fields
89 let (_, source_map) = db.body_with_source_map(self.func.into());
90
91 if let Ok(source_ptr) = source_map.expr_syntax(id) {
92 if let Some(expr) = source_ptr.value.as_ref().left() {
93 let root = source_ptr.file_syntax(db.upcast());
94 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
95 if let Some(field_list) = record_lit.record_field_list() {
96 let variant_data = variant_data(db.upcast(), variant_def);
97 let missed_fields = missed_fields
98 .into_iter()
99 .map(|idx| variant_data.fields()[idx].name.clone())
100 .collect();
101 self.sink.push(MissingFields {
102 file: source_ptr.file_id,
103 field_list: AstPtr::new(&field_list),
104 missed_fields,
105 })
106 }
107 }
108 }
109 }
110 }
111
112 fn create_record_pattern_missing_fields_diagnostic(
113 &mut self,
114 id: PatId,
115 db: &dyn HirDatabase,
116 variant_def: VariantId,
117 missed_fields: Vec<LocalStructFieldId>,
118 ) {
119 // XXX: only look at source_map if we do have missing fields
120 let (_, source_map) = db.body_with_source_map(self.func.into());
121
122 if let Ok(source_ptr) = source_map.pat_syntax(id) {
123 if let Some(expr) = source_ptr.value.as_ref().left() {
124 let root = source_ptr.file_syntax(db.upcast());
125 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
126 if let Some(field_list) = record_pat.record_field_pat_list() {
127 let variant_data = variant_data(db.upcast(), variant_def);
128 let missed_fields = missed_fields
129 .into_iter()
130 .map(|idx| variant_data.fields()[idx].name.clone())
131 .collect();
132 self.sink.push(MissingPatFields {
133 file: source_ptr.file_id,
134 field_list: AstPtr::new(&field_list),
135 missed_fields,
136 })
137 }
138 }
139 }
140 }
141 }
142
85 fn validate_match( 143 fn validate_match(
86 &mut self, 144 &mut self,
87 id: ExprId, 145 id: ExprId,
@@ -147,7 +205,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
147 } 205 }
148 206
149 if let Ok(source_ptr) = source_map.expr_syntax(id) { 207 if let Ok(source_ptr) = source_map.expr_syntax(id) {
150 if let Some(expr) = source_ptr.value.left() { 208 if let Some(expr) = source_ptr.value.as_ref().left() {
151 let root = source_ptr.file_syntax(db.upcast()); 209 let root = source_ptr.file_syntax(db.upcast());
152 if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) { 210 if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) {
153 if let (Some(match_expr), Some(arms)) = 211 if let (Some(match_expr), Some(arms)) =
@@ -232,9 +290,9 @@ pub fn record_pattern_missing_fields(
232 infer: &InferenceResult, 290 infer: &InferenceResult,
233 id: PatId, 291 id: PatId,
234 pat: &Pat, 292 pat: &Pat,
235) -> Option<(VariantId, Vec<LocalStructFieldId>)> { 293) -> Option<(VariantId, Vec<LocalStructFieldId>, /*exhaustive*/ bool)> {
236 let fields = match pat { 294 let (fields, exhaustive) = match pat {
237 Pat::Record { path: _, args } => args, 295 Pat::Record { path: _, args, ellipsis } => (args, !ellipsis),
238 _ => return None, 296 _ => return None,
239 }; 297 };
240 298
@@ -254,5 +312,5 @@ pub fn record_pattern_missing_fields(
254 if missed_fields.is_empty() { 312 if missed_fields.is_empty() {
255 return None; 313 return None;
256 } 314 }
257 Some((variant_def, missed_fields)) 315 Some((variant_def, missed_fields, exhaustive))
258} 316}
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 69bbb4307..078476f76 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -158,7 +158,7 @@ impl<'a> InferenceContext<'a> {
158 Pat::TupleStruct { path: p, args: subpats } => { 158 Pat::TupleStruct { path: p, args: subpats } => {
159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) 159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat)
160 } 160 }
161 Pat::Record { path: p, args: fields } => { 161 Pat::Record { path: p, args: fields, ellipsis: _ } => {
162 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) 162 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat)
163 } 163 }
164 Pat::Path(path) => { 164 Pat::Path(path) => {
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index f97e0bfeb..54e31602f 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -23,7 +23,7 @@ use insta::assert_snapshot;
23use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase}; 23use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase};
24use ra_syntax::{ 24use ra_syntax::{
25 algo, 25 algo,
26 ast::{self, AstNode, AstToken}, 26 ast::{self, AstNode},
27}; 27};
28use stdx::format_to; 28use stdx::format_to;
29 29
@@ -87,7 +87,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
87 } 87 }
88 Err(SyntheticSyntax) => continue, 88 Err(SyntheticSyntax) => continue,
89 }; 89 };
90 types.push((syntax_ptr, ty)); 90 types.push((syntax_ptr.clone(), ty));
91 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { 91 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
92 mismatches.push((syntax_ptr, mismatch)); 92 mismatches.push((syntax_ptr, mismatch));
93 } 93 }
@@ -101,7 +101,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
101 let node = src_ptr.value.to_node(&src_ptr.file_syntax(&db)); 101 let node = src_ptr.value.to_node(&src_ptr.file_syntax(&db));
102 102
103 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) { 103 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) {
104 (self_param.self_kw_token().unwrap().syntax().text_range(), "self".to_string()) 104 (self_param.self_token().unwrap().text_range(), "self".to_string())
105 } else { 105 } else {
106 (src_ptr.value.range(), node.text().to_string().replace("\n", " ")) 106 (src_ptr.value.range(), node.text().to_string().replace("\n", " "))
107 }; 107 };
@@ -349,3 +349,103 @@ fn no_such_field_with_feature_flag_diagnostics() {
349 349
350 assert_snapshot!(diagnostics, @r###""###); 350 assert_snapshot!(diagnostics, @r###""###);
351} 351}
352
353#[test]
354fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
355 let diagnostics = TestDB::with_files(
356 r#"
357 //- /lib.rs crate:foo cfg:feature=foo
358 struct S {
359 #[cfg(feature = "foo")]
360 foo: u32,
361 #[cfg(not(feature = "foo"))]
362 bar: u32,
363 }
364
365 impl S {
366 #[cfg(feature = "foo")]
367 fn new(foo: u32) -> Self {
368 Self { foo }
369 }
370 #[cfg(not(feature = "foo"))]
371 fn new(bar: u32) -> Self {
372 Self { bar }
373 }
374 }
375 "#,
376 )
377 .diagnostics()
378 .0;
379
380 assert_snapshot!(diagnostics, @r###""###);
381}
382
383#[test]
384fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
385 let diagnostics = TestDB::with_files(
386 r#"
387 //- /lib.rs crate:foo cfg:feature=foo
388 struct S {
389 #[cfg(feature = "foo")]
390 foo: u32,
391 #[cfg(not(feature = "foo"))]
392 bar: u32,
393 }
394
395 impl S {
396 fn new(val: u32) -> Self {
397 Self {
398 #[cfg(feature = "foo")]
399 foo: val,
400 #[cfg(not(feature = "foo"))]
401 bar: val,
402 }
403 }
404 }
405 "#,
406 )
407 .diagnostics()
408 .0;
409
410 assert_snapshot!(diagnostics, @r###""###);
411}
412
413#[test]
414fn missing_record_pat_field_diagnostic() {
415 let diagnostics = TestDB::with_files(
416 r"
417 //- /lib.rs
418 struct S { foo: i32, bar: () }
419 fn baz(s: S) {
420 let S { foo: _ } = s;
421 }
422 ",
423 )
424 .diagnostics()
425 .0;
426
427 assert_snapshot!(diagnostics, @r###"
428 "{ foo: _ }": Missing structure fields:
429 - bar
430 "###
431 );
432}
433
434#[test]
435fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
436 let diagnostics = TestDB::with_files(
437 r"
438 //- /lib.rs
439 struct S { foo: i32, bar: () }
440 fn baz(s: S) -> i32 {
441 match s {
442 S { foo, .. } => foo,
443 }
444 }
445 ",
446 )
447 .diagnostics()
448 .0;
449
450 assert_snapshot!(diagnostics, @"");
451}
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index ff4599b71..f2a9b1c40 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -1,10 +1,13 @@
1use std::fs;
2
1use insta::assert_snapshot; 3use insta::assert_snapshot;
2use ra_db::fixture::WithFixture; 4use ra_db::fixture::WithFixture;
3 5use test_utils::project_dir;
4use super::{infer, type_at, type_at_pos};
5 6
6use crate::test_db::TestDB; 7use crate::test_db::TestDB;
7 8
9use super::{infer, type_at, type_at_pos};
10
8#[test] 11#[test]
9fn cfg_impl_def() { 12fn cfg_impl_def() {
10 let (db, pos) = TestDB::with_position( 13 let (db, pos) = TestDB::with_position(
@@ -482,6 +485,30 @@ fn bar() -> u32 {0}
482} 485}
483 486
484#[test] 487#[test]
488#[ignore]
489fn include_accidentally_quadratic() {
490 let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic");
491 let big_file = fs::read_to_string(file).unwrap();
492 let big_file = vec![big_file; 10].join("\n");
493
494 let fixture = r#"
495//- /main.rs
496#[rustc_builtin_macro]
497macro_rules! include {() => {}}
498
499include!("foo.rs");
500
501fn main() {
502 RegisterBlock { }<|>;
503}
504 "#;
505 let fixture = format!("{}\n//- /foo.rs\n{}", fixture, big_file);
506
507 let (db, pos) = TestDB::with_position(&fixture);
508 assert_eq!("RegisterBlock", type_at_pos(&db, pos));
509}
510
511#[test]
485fn infer_builtin_macros_include_concat() { 512fn infer_builtin_macros_include_concat() {
486 let (db, pos) = TestDB::with_position( 513 let (db, pos) = TestDB::with_position(
487 r#" 514 r#"
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 5a1e12ce9..21e233379 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -177,7 +177,7 @@ fn solve(
177 177
178 let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL); 178 let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL);
179 179
180 let solution = solver.solve_limited(&context, goal, || { 180 let should_continue = || {
181 context.db.check_canceled(); 181 context.db.check_canceled();
182 let remaining = fuel.get(); 182 let remaining = fuel.get();
183 fuel.set(remaining - 1); 183 fuel.set(remaining - 1);
@@ -185,12 +185,21 @@ fn solve(
185 log::debug!("fuel exhausted"); 185 log::debug!("fuel exhausted");
186 } 186 }
187 remaining > 0 187 remaining > 0
188 }); 188 };
189 let mut solve = || solver.solve_limited(&context, goal, should_continue);
190 // don't set the TLS for Chalk unless Chalk debugging is active, to make
191 // extra sure we only use it for debugging
192 let solution =
193 if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() };
189 194
190 log::debug!("solve({:?}) => {:?}", goal, solution); 195 log::debug!("solve({:?}) => {:?}", goal, solution);
191 solution 196 solution
192} 197}
193 198
199fn is_chalk_debug() -> bool {
200 std::env::var("CHALK_DEBUG").is_ok()
201}
202
194fn solution_from_chalk( 203fn solution_from_chalk(
195 db: &dyn HirDatabase, 204 db: &dyn HirDatabase,
196 solution: chalk_solve::Solution<Interner>, 205 solution: chalk_solve::Solution<Interner>,
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 1bc0f0713..c5f1b5232 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -20,6 +20,8 @@ use crate::{
20 ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 20 ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
21}; 21};
22 22
23pub(super) mod tls;
24
23#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] 25#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
24pub struct Interner; 26pub struct Interner;
25 27
@@ -33,90 +35,85 @@ impl chalk_ir::interner::Interner for Interner {
33 type Identifier = TypeAliasId; 35 type Identifier = TypeAliasId;
34 type DefId = InternId; 36 type DefId = InternId;
35 37
36 // FIXME: implement these
37 fn debug_struct_id( 38 fn debug_struct_id(
38 _type_kind_id: chalk_ir::StructId<Self>, 39 type_kind_id: StructId,
39 _fmt: &mut fmt::Formatter<'_>, 40 fmt: &mut fmt::Formatter<'_>,
40 ) -> Option<fmt::Result> { 41 ) -> Option<fmt::Result> {
41 None 42 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
42 } 43 }
43 44
44 fn debug_trait_id( 45 fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
45 _type_kind_id: chalk_ir::TraitId<Self>, 46 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt)))
46 _fmt: &mut fmt::Formatter<'_>,
47 ) -> Option<fmt::Result> {
48 None
49 } 47 }
50 48
51 fn debug_assoc_type_id( 49 fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
52 _id: chalk_ir::AssocTypeId<Self>, 50 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
53 _fmt: &mut fmt::Formatter<'_>,
54 ) -> Option<fmt::Result> {
55 None
56 } 51 }
57 52
58 fn debug_alias( 53 fn debug_alias(
59 _projection: &chalk_ir::AliasTy<Self>, 54 alias: &chalk_ir::AliasTy<Interner>,
60 _fmt: &mut fmt::Formatter<'_>, 55 fmt: &mut fmt::Formatter<'_>,
61 ) -> Option<fmt::Result> { 56 ) -> Option<fmt::Result> {
62 None 57 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
63 } 58 }
64 59
65 fn debug_ty(_ty: &chalk_ir::Ty<Self>, _fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { 60 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
66 None 61 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
67 } 62 }
68 63
69 fn debug_lifetime( 64 fn debug_lifetime(
70 _lifetime: &chalk_ir::Lifetime<Self>, 65 lifetime: &chalk_ir::Lifetime<Interner>,
71 _fmt: &mut fmt::Formatter<'_>, 66 fmt: &mut fmt::Formatter<'_>,
72 ) -> Option<fmt::Result> { 67 ) -> Option<fmt::Result> {
73 None 68 tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
74 } 69 }
75 70
76 fn debug_parameter( 71 fn debug_parameter(
77 _parameter: &Parameter<Self>, 72 parameter: &Parameter<Interner>,
78 _fmt: &mut fmt::Formatter<'_>, 73 fmt: &mut fmt::Formatter<'_>,
79 ) -> Option<fmt::Result> { 74 ) -> Option<fmt::Result> {
80 None 75 tls::with_current_program(|prog| Some(prog?.debug_parameter(parameter, fmt)))
81 } 76 }
82 77
83 fn debug_goal(_goal: &Goal<Self>, _fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { 78 fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
84 None 79 tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
85 } 80 }
86 81
87 fn debug_goals( 82 fn debug_goals(
88 _goals: &chalk_ir::Goals<Self>, 83 goals: &chalk_ir::Goals<Interner>,
89 _fmt: &mut fmt::Formatter<'_>, 84 fmt: &mut fmt::Formatter<'_>,
90 ) -> Option<fmt::Result> { 85 ) -> Option<fmt::Result> {
91 None 86 tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
92 } 87 }
93 88
94 fn debug_program_clause_implication( 89 fn debug_program_clause_implication(
95 _pci: &chalk_ir::ProgramClauseImplication<Self>, 90 pci: &chalk_ir::ProgramClauseImplication<Interner>,
96 _fmt: &mut fmt::Formatter<'_>, 91 fmt: &mut fmt::Formatter<'_>,
97 ) -> Option<fmt::Result> { 92 ) -> Option<fmt::Result> {
98 None 93 tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
99 } 94 }
100 95
101 fn debug_application_ty( 96 fn debug_application_ty(
102 _application_ty: &chalk_ir::ApplicationTy<Self>, 97 application_ty: &chalk_ir::ApplicationTy<Interner>,
103 _fmt: &mut fmt::Formatter<'_>, 98 fmt: &mut fmt::Formatter<'_>,
104 ) -> Option<fmt::Result> { 99 ) -> Option<fmt::Result> {
105 None 100 tls::with_current_program(|prog| Some(prog?.debug_application_ty(application_ty, fmt)))
106 } 101 }
107 102
108 fn debug_substitution( 103 fn debug_substitution(
109 _substitution: &chalk_ir::Substitution<Self>, 104 substitution: &chalk_ir::Substitution<Interner>,
110 _fmt: &mut fmt::Formatter<'_>, 105 fmt: &mut fmt::Formatter<'_>,
111 ) -> Option<fmt::Result> { 106 ) -> Option<fmt::Result> {
112 None 107 tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
113 } 108 }
114 109
115 fn debug_separator_trait_ref( 110 fn debug_separator_trait_ref(
116 _separator_trait_ref: &chalk_ir::SeparatorTraitRef<Self>, 111 separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
117 _fmt: &mut fmt::Formatter<'_>, 112 fmt: &mut fmt::Formatter<'_>,
118 ) -> Option<fmt::Result> { 113 ) -> Option<fmt::Result> {
119 None 114 tls::with_current_program(|prog| {
115 Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
116 })
120 } 117 }
121 118
122 fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> { 119 fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
new file mode 100644
index 000000000..d9bbb54a5
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -0,0 +1,231 @@
1//! Implementation of Chalk debug helper functions using TLS.
2use std::fmt;
3
4use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName};
5
6use super::{from_chalk, Interner};
7use crate::{db::HirDatabase, CallableDef, TypeCtor};
8use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId};
9
10pub use unsafe_tls::{set_current_program, with_current_program};
11
12pub struct DebugContext<'a>(&'a (dyn HirDatabase + 'a));
13
14impl DebugContext<'_> {
15 pub fn debug_struct_id(
16 &self,
17 id: super::StructId,
18 f: &mut fmt::Formatter<'_>,
19 ) -> Result<(), fmt::Error> {
20 let type_ctor: TypeCtor = from_chalk(self.0, TypeName::Struct(id));
21 match type_ctor {
22 TypeCtor::Bool => write!(f, "bool")?,
23 TypeCtor::Char => write!(f, "char")?,
24 TypeCtor::Int(t) => write!(f, "{}", t)?,
25 TypeCtor::Float(t) => write!(f, "{}", t)?,
26 TypeCtor::Str => write!(f, "str")?,
27 TypeCtor::Slice => write!(f, "slice")?,
28 TypeCtor::Array => write!(f, "array")?,
29 TypeCtor::RawPtr(m) => write!(f, "*{}", m.as_keyword_for_ptr())?,
30 TypeCtor::Ref(m) => write!(f, "&{}", m.as_keyword_for_ref())?,
31 TypeCtor::Never => write!(f, "!")?,
32 TypeCtor::Tuple { .. } => {
33 write!(f, "()")?;
34 }
35 TypeCtor::FnPtr { .. } => {
36 write!(f, "fn")?;
37 }
38 TypeCtor::FnDef(def) => {
39 let name = match def {
40 CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(),
41 CallableDef::StructId(s) => self.0.struct_data(s).name.clone(),
42 CallableDef::EnumVariantId(e) => {
43 let enum_data = self.0.enum_data(e.parent);
44 enum_data.variants[e.local_id].name.clone()
45 }
46 };
47 match def {
48 CallableDef::FunctionId(_) => write!(f, "{{fn {}}}", name)?,
49 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {
50 write!(f, "{{ctor {}}}", name)?
51 }
52 }
53 }
54 TypeCtor::Adt(def_id) => {
55 let name = match def_id {
56 AdtId::StructId(it) => self.0.struct_data(it).name.clone(),
57 AdtId::UnionId(it) => self.0.union_data(it).name.clone(),
58 AdtId::EnumId(it) => self.0.enum_data(it).name.clone(),
59 };
60 write!(f, "{}", name)?;
61 }
62 TypeCtor::AssociatedType(type_alias) => {
63 let trait_ = match type_alias.lookup(self.0.upcast()).container {
64 AssocContainerId::TraitId(it) => it,
65 _ => panic!("not an associated type"),
66 };
67 let trait_name = self.0.trait_data(trait_).name.clone();
68 let name = self.0.type_alias_data(type_alias).name.clone();
69 write!(f, "{}::{}", trait_name, name)?;
70 }
71 TypeCtor::Closure { def, expr } => {
72 write!(f, "{{closure {:?} in {:?}}}", expr.into_raw(), def)?;
73 }
74 }
75 Ok(())
76 }
77
78 pub fn debug_trait_id(
79 &self,
80 id: super::TraitId,
81 fmt: &mut fmt::Formatter<'_>,
82 ) -> Result<(), fmt::Error> {
83 let trait_: hir_def::TraitId = from_chalk(self.0, id);
84 let trait_data = self.0.trait_data(trait_);
85 write!(fmt, "{}", trait_data.name)
86 }
87
88 pub fn debug_assoc_type_id(
89 &self,
90 id: super::AssocTypeId,
91 fmt: &mut fmt::Formatter<'_>,
92 ) -> Result<(), fmt::Error> {
93 let type_alias: TypeAliasId = from_chalk(self.0, id);
94 let type_alias_data = self.0.type_alias_data(type_alias);
95 let trait_ = match type_alias.lookup(self.0.upcast()).container {
96 AssocContainerId::TraitId(t) => t,
97 _ => panic!("associated type not in trait"),
98 };
99 let trait_data = self.0.trait_data(trait_);
100 write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
101 }
102
103 pub fn debug_alias(
104 &self,
105 alias: &AliasTy<Interner>,
106 fmt: &mut fmt::Formatter<'_>,
107 ) -> Result<(), fmt::Error> {
108 let type_alias: TypeAliasId = from_chalk(self.0, alias.associated_ty_id);
109 let type_alias_data = self.0.type_alias_data(type_alias);
110 let trait_ = match type_alias.lookup(self.0.upcast()).container {
111 AssocContainerId::TraitId(t) => t,
112 _ => panic!("associated type not in trait"),
113 };
114 let trait_data = self.0.trait_data(trait_);
115 let params = alias.substitution.parameters(&Interner);
116 write!(
117 fmt,
118 "<{:?} as {}<{:?}>>::{}",
119 &params[0],
120 trait_data.name,
121 &params[1..],
122 type_alias_data.name
123 )
124 }
125
126 pub fn debug_ty(
127 &self,
128 ty: &chalk_ir::Ty<Interner>,
129 fmt: &mut fmt::Formatter<'_>,
130 ) -> Result<(), fmt::Error> {
131 write!(fmt, "{:?}", ty.data(&Interner))
132 }
133
134 pub fn debug_lifetime(
135 &self,
136 lifetime: &Lifetime<Interner>,
137 fmt: &mut fmt::Formatter<'_>,
138 ) -> Result<(), fmt::Error> {
139 write!(fmt, "{:?}", lifetime.data(&Interner))
140 }
141
142 pub fn debug_parameter(
143 &self,
144 parameter: &Parameter<Interner>,
145 fmt: &mut fmt::Formatter<'_>,
146 ) -> Result<(), fmt::Error> {
147 write!(fmt, "{:?}", parameter.data(&Interner).inner_debug())
148 }
149
150 pub fn debug_goal(
151 &self,
152 goal: &Goal<Interner>,
153 fmt: &mut fmt::Formatter<'_>,
154 ) -> Result<(), fmt::Error> {
155 let goal_data = goal.data(&Interner);
156 write!(fmt, "{:?}", goal_data)
157 }
158
159 pub fn debug_goals(
160 &self,
161 goals: &Goals<Interner>,
162 fmt: &mut fmt::Formatter<'_>,
163 ) -> Result<(), fmt::Error> {
164 write!(fmt, "{:?}", goals.debug(&Interner))
165 }
166
167 pub fn debug_program_clause_implication(
168 &self,
169 pci: &ProgramClauseImplication<Interner>,
170 fmt: &mut fmt::Formatter<'_>,
171 ) -> Result<(), fmt::Error> {
172 write!(fmt, "{:?}", pci.debug(&Interner))
173 }
174
175 pub fn debug_application_ty(
176 &self,
177 application_ty: &chalk_ir::ApplicationTy<Interner>,
178 fmt: &mut fmt::Formatter<'_>,
179 ) -> Result<(), fmt::Error> {
180 write!(fmt, "{:?}", application_ty.debug(&Interner))
181 }
182
183 pub fn debug_substitution(
184 &self,
185 substitution: &chalk_ir::Substitution<Interner>,
186 fmt: &mut fmt::Formatter<'_>,
187 ) -> Result<(), fmt::Error> {
188 write!(fmt, "{:?}", substitution.debug(&Interner))
189 }
190
191 pub fn debug_separator_trait_ref(
192 &self,
193 separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
194 fmt: &mut fmt::Formatter<'_>,
195 ) -> Result<(), fmt::Error> {
196 write!(fmt, "{:?}", separator_trait_ref.debug(&Interner))
197 }
198}
199
200mod unsafe_tls {
201 use super::DebugContext;
202 use crate::db::HirDatabase;
203 use scoped_tls::scoped_thread_local;
204
205 scoped_thread_local!(static PROGRAM: DebugContext);
206
207 pub fn with_current_program<R>(
208 op: impl for<'a> FnOnce(Option<&'a DebugContext<'a>>) -> R,
209 ) -> R {
210 if PROGRAM.is_set() {
211 PROGRAM.with(|prog| op(Some(prog)))
212 } else {
213 op(None)
214 }
215 }
216
217 pub fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R
218 where
219 OP: FnOnce() -> R,
220 {
221 let ctx = DebugContext(p);
222 // we're transmuting the lifetime in the DebugContext to static. This is
223 // fine because we only keep the reference for the lifetime of this
224 // function, *and* the only way to access the context is through
225 // `with_current_program`, which hides the lifetime through the `for`
226 // type.
227 let static_p: &DebugContext<'static> =
228 unsafe { std::mem::transmute::<&DebugContext, &DebugContext<'static>>(&ctx) };
229 PROGRAM.set(static_p, || op())
230 }
231}