]>
Commit | Line | Data |
---|---|---|
353b0b11 FG |
1 | //! This module generates a polymorphic MIR from a hir body |
2 | ||
fe692bf9 | 3 | use std::{fmt::Write, iter, mem}; |
353b0b11 | 4 | |
fe692bf9 | 5 | use base_db::FileId; |
353b0b11 FG |
6 | use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; |
7 | use hir_def::{ | |
8 | body::Body, | |
fe692bf9 FG |
9 | data::adt::{StructKind, VariantData}, |
10 | hir::{ | |
11 | ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, | |
12 | LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, | |
353b0b11 FG |
13 | }, |
14 | lang_item::{LangItem, LangItemTarget}, | |
353b0b11 | 15 | path::Path, |
fe692bf9 FG |
16 | resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs}, |
17 | AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, | |
18 | TraitId, TypeOrConstParamId, | |
353b0b11 FG |
19 | }; |
20 | use hir_expand::name::Name; | |
21 | use la_arena::ArenaMap; | |
fe692bf9 FG |
22 | use rustc_hash::FxHashMap; |
23 | use syntax::TextRange; | |
24 | use triomphe::Arc; | |
353b0b11 FG |
25 | |
26 | use crate::{ | |
fe692bf9 FG |
27 | consteval::ConstEvalError, |
28 | db::HirDatabase, | |
29 | display::HirDisplay, | |
30 | infer::{CaptureKind, CapturedItem, TypeMismatch}, | |
31 | inhabitedness::is_ty_uninhabited_from, | |
32 | layout::LayoutError, | |
33 | mapping::ToChalk, | |
34 | static_lifetime, | |
35 | traits::FnTrait, | |
36 | utils::{generics, ClosureSubst}, | |
37 | Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, | |
353b0b11 FG |
38 | }; |
39 | ||
40 | use super::*; | |
41 | ||
42 | mod as_place; | |
fe692bf9 | 43 | mod pattern_matching; |
353b0b11 | 44 | |
fe692bf9 | 45 | #[derive(Debug, Clone)] |
353b0b11 FG |
46 | struct LoopBlocks { |
47 | begin: BasicBlockId, | |
48 | /// `None` for loops that are not terminating | |
49 | end: Option<BasicBlockId>, | |
fe692bf9 FG |
50 | place: Place, |
51 | drop_scope_index: usize, | |
52 | } | |
53 | ||
54 | #[derive(Debug, Clone, Default)] | |
55 | struct DropScope { | |
56 | /// locals, in order of definition (so we should run drop glues in reverse order) | |
57 | locals: Vec<LocalId>, | |
353b0b11 FG |
58 | } |
59 | ||
60 | struct MirLowerCtx<'a> { | |
61 | result: MirBody, | |
62 | owner: DefWithBodyId, | |
63 | current_loop_blocks: Option<LoopBlocks>, | |
fe692bf9 | 64 | labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>, |
353b0b11 FG |
65 | discr_temp: Option<Place>, |
66 | db: &'a dyn HirDatabase, | |
67 | body: &'a Body, | |
68 | infer: &'a InferenceResult, | |
fe692bf9 | 69 | drop_scopes: Vec<DropScope>, |
353b0b11 FG |
70 | } |
71 | ||
72 | #[derive(Debug, Clone, PartialEq, Eq)] | |
73 | pub enum MirLowerError { | |
fe692bf9 | 74 | ConstEvalError(String, Box<ConstEvalError>), |
353b0b11 FG |
75 | LayoutError(LayoutError), |
76 | IncompleteExpr, | |
fe692bf9 FG |
77 | IncompletePattern, |
78 | /// Trying to lower a trait function, instead of an implementation | |
79 | TraitFunctionDefinition(TraitId, Name), | |
353b0b11 FG |
80 | UnresolvedName(String), |
81 | RecordLiteralWithoutPath, | |
fe692bf9 | 82 | UnresolvedMethod(String), |
353b0b11 | 83 | UnresolvedField, |
fe692bf9 FG |
84 | UnsizedTemporary(Ty), |
85 | MissingFunctionDefinition(DefWithBodyId, ExprId), | |
353b0b11 FG |
86 | TypeMismatch(TypeMismatch), |
87 | /// This should be never happen. Type mismatch should catch everything. | |
88 | TypeError(&'static str), | |
89 | NotSupported(String), | |
90 | ContinueWithoutLoop, | |
91 | BreakWithoutLoop, | |
92 | Loop, | |
93 | /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened | |
fe692bf9 | 94 | ImplementationError(String), |
353b0b11 FG |
95 | LangItemNotFound(LangItem), |
96 | MutatingRvalue, | |
fe692bf9 FG |
97 | UnresolvedLabel, |
98 | UnresolvedUpvar(Place), | |
99 | UnaccessableLocal, | |
100 | ||
101 | // monomorphization errors: | |
102 | GenericArgNotProvided(TypeOrConstParamId, Substitution), | |
103 | } | |
104 | ||
105 | /// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves. | |
106 | struct DropScopeToken; | |
107 | impl DropScopeToken { | |
108 | fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId { | |
109 | std::mem::forget(self); | |
110 | ctx.pop_drop_scope_internal(current) | |
111 | } | |
112 | ||
113 | /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop | |
114 | /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled | |
115 | /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't | |
116 | /// do anything) | |
117 | fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_>) { | |
118 | std::mem::forget(self); | |
119 | ctx.pop_drop_scope_assume_dropped_internal(); | |
120 | } | |
121 | } | |
122 | ||
123 | // Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since | |
124 | // in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be | |
125 | // actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful | |
126 | // stack trace. | |
127 | // | |
128 | // impl Drop for DropScopeToken { | |
129 | // fn drop(&mut self) { | |
130 | // never!("Drop scope doesn't popped"); | |
131 | // } | |
132 | // } | |
133 | ||
134 | impl MirLowerError { | |
135 | pub fn pretty_print( | |
136 | &self, | |
137 | f: &mut String, | |
138 | db: &dyn HirDatabase, | |
139 | span_formatter: impl Fn(FileId, TextRange) -> String, | |
140 | ) -> std::result::Result<(), std::fmt::Error> { | |
141 | match self { | |
142 | MirLowerError::ConstEvalError(name, e) => { | |
143 | writeln!(f, "In evaluating constant {name}")?; | |
144 | match &**e { | |
145 | ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?, | |
146 | ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?, | |
147 | } | |
148 | } | |
149 | MirLowerError::MissingFunctionDefinition(owner, x) => { | |
150 | let body = db.body(*owner); | |
151 | writeln!( | |
152 | f, | |
153 | "Missing function definition for {}", | |
154 | body.pretty_print_expr(db.upcast(), *owner, *x) | |
155 | )?; | |
156 | } | |
157 | MirLowerError::TypeMismatch(e) => { | |
158 | writeln!( | |
159 | f, | |
160 | "Type mismatch: Expected {}, found {}", | |
161 | e.expected.display(db), | |
162 | e.actual.display(db), | |
163 | )?; | |
164 | } | |
165 | MirLowerError::GenericArgNotProvided(id, subst) => { | |
166 | let parent = id.parent; | |
167 | let param = &db.generic_params(parent).type_or_consts[id.local_id]; | |
168 | writeln!( | |
169 | f, | |
170 | "Generic arg not provided for {}", | |
171 | param.name().unwrap_or(&Name::missing()).display(db.upcast()) | |
172 | )?; | |
173 | writeln!(f, "Provided args: [")?; | |
174 | for g in subst.iter(Interner) { | |
175 | write!(f, " {},", g.display(db).to_string())?; | |
176 | } | |
177 | writeln!(f, "]")?; | |
178 | } | |
179 | MirLowerError::LayoutError(_) | |
180 | | MirLowerError::UnsizedTemporary(_) | |
181 | | MirLowerError::IncompleteExpr | |
182 | | MirLowerError::IncompletePattern | |
183 | | MirLowerError::UnaccessableLocal | |
184 | | MirLowerError::TraitFunctionDefinition(_, _) | |
185 | | MirLowerError::UnresolvedName(_) | |
186 | | MirLowerError::RecordLiteralWithoutPath | |
187 | | MirLowerError::UnresolvedMethod(_) | |
188 | | MirLowerError::UnresolvedField | |
189 | | MirLowerError::TypeError(_) | |
190 | | MirLowerError::NotSupported(_) | |
191 | | MirLowerError::ContinueWithoutLoop | |
192 | | MirLowerError::BreakWithoutLoop | |
193 | | MirLowerError::Loop | |
194 | | MirLowerError::ImplementationError(_) | |
195 | | MirLowerError::LangItemNotFound(_) | |
196 | | MirLowerError::MutatingRvalue | |
197 | | MirLowerError::UnresolvedLabel | |
198 | | MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?, | |
199 | } | |
200 | Ok(()) | |
201 | } | |
353b0b11 FG |
202 | } |
203 | ||
204 | macro_rules! not_supported { | |
205 | ($x: expr) => { | |
206 | return Err(MirLowerError::NotSupported(format!($x))) | |
207 | }; | |
208 | } | |
209 | ||
210 | macro_rules! implementation_error { | |
211 | ($x: expr) => {{ | |
fe692bf9 FG |
212 | ::stdx::never!("MIR lower implementation bug: {}", format!($x)); |
213 | return Err(MirLowerError::ImplementationError(format!($x))); | |
353b0b11 FG |
214 | }}; |
215 | } | |
216 | ||
353b0b11 FG |
217 | impl From<LayoutError> for MirLowerError { |
218 | fn from(value: LayoutError) -> Self { | |
219 | MirLowerError::LayoutError(value) | |
220 | } | |
221 | } | |
222 | ||
223 | impl MirLowerError { | |
224 | fn unresolved_path(db: &dyn HirDatabase, p: &Path) -> Self { | |
225 | Self::UnresolvedName(p.display(db).to_string()) | |
226 | } | |
227 | } | |
228 | ||
229 | type Result<T> = std::result::Result<T, MirLowerError>; | |
230 | ||
fe692bf9 FG |
231 | impl<'ctx> MirLowerCtx<'ctx> { |
232 | fn new( | |
233 | db: &'ctx dyn HirDatabase, | |
234 | owner: DefWithBodyId, | |
235 | body: &'ctx Body, | |
236 | infer: &'ctx InferenceResult, | |
237 | ) -> Self { | |
238 | let mut basic_blocks = Arena::new(); | |
239 | let start_block = basic_blocks.alloc(BasicBlock { | |
240 | statements: vec![], | |
241 | terminator: None, | |
242 | is_cleanup: false, | |
243 | }); | |
244 | let locals = Arena::new(); | |
245 | let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new(); | |
246 | let mir = MirBody { | |
247 | basic_blocks, | |
248 | locals, | |
249 | start_block, | |
250 | binding_locals, | |
251 | param_locals: vec![], | |
252 | owner, | |
253 | closures: vec![], | |
254 | }; | |
255 | let ctx = MirLowerCtx { | |
256 | result: mir, | |
257 | db, | |
258 | infer, | |
259 | body, | |
260 | owner, | |
261 | current_loop_blocks: None, | |
262 | labeled_loop_blocks: Default::default(), | |
263 | discr_temp: None, | |
264 | drop_scopes: vec![DropScope::default()], | |
265 | }; | |
266 | ctx | |
267 | } | |
268 | ||
269 | fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result<LocalId> { | |
353b0b11 | 270 | if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { |
fe692bf9 | 271 | return Err(MirLowerError::UnsizedTemporary(ty)); |
353b0b11 | 272 | } |
fe692bf9 FG |
273 | let l = self.result.locals.alloc(Local { ty }); |
274 | self.push_storage_live_for_local(l, current, span)?; | |
275 | Ok(l) | |
353b0b11 FG |
276 | } |
277 | ||
278 | fn lower_expr_to_some_operand( | |
279 | &mut self, | |
280 | expr_id: ExprId, | |
281 | current: BasicBlockId, | |
282 | ) -> Result<Option<(Operand, BasicBlockId)>> { | |
283 | if !self.has_adjustments(expr_id) { | |
284 | match &self.body.exprs[expr_id] { | |
285 | Expr::Literal(l) => { | |
fe692bf9 | 286 | let ty = self.expr_ty_without_adjust(expr_id); |
353b0b11 FG |
287 | return Ok(Some((self.lower_literal_to_operand(ty, l)?, current))); |
288 | } | |
289 | _ => (), | |
290 | } | |
291 | } | |
292 | let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { | |
293 | return Ok(None); | |
294 | }; | |
295 | Ok(Some((Operand::Copy(p), current))) | |
296 | } | |
297 | ||
298 | fn lower_expr_to_place_with_adjust( | |
299 | &mut self, | |
300 | expr_id: ExprId, | |
301 | place: Place, | |
302 | current: BasicBlockId, | |
303 | adjustments: &[Adjustment], | |
304 | ) -> Result<Option<BasicBlockId>> { | |
305 | match adjustments.split_last() { | |
306 | Some((last, rest)) => match &last.kind { | |
307 | Adjust::NeverToAny => { | |
fe692bf9 FG |
308 | let temp = |
309 | self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?; | |
353b0b11 FG |
310 | self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) |
311 | } | |
312 | Adjust::Deref(_) => { | |
313 | let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else { | |
314 | return Ok(None); | |
315 | }; | |
316 | self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); | |
317 | Ok(Some(current)) | |
318 | } | |
319 | Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => { | |
320 | let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { | |
321 | return Ok(None); | |
322 | }; | |
323 | let bk = BorrowKind::from_chalk(*m); | |
324 | self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); | |
325 | Ok(Some(current)) | |
326 | } | |
327 | Adjust::Pointer(cast) => { | |
328 | let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { | |
329 | return Ok(None); | |
330 | }; | |
331 | self.push_assignment( | |
332 | current, | |
333 | place, | |
334 | Rvalue::Cast( | |
335 | CastKind::Pointer(cast.clone()), | |
336 | Operand::Copy(p).into(), | |
337 | last.target.clone(), | |
338 | ), | |
339 | expr_id.into(), | |
340 | ); | |
341 | Ok(Some(current)) | |
342 | } | |
343 | }, | |
344 | None => self.lower_expr_to_place_without_adjust(expr_id, place, current), | |
345 | } | |
346 | } | |
347 | ||
348 | fn lower_expr_to_place( | |
349 | &mut self, | |
350 | expr_id: ExprId, | |
351 | place: Place, | |
352 | prev_block: BasicBlockId, | |
353 | ) -> Result<Option<BasicBlockId>> { | |
354 | if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { | |
355 | return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments); | |
356 | } | |
357 | self.lower_expr_to_place_without_adjust(expr_id, place, prev_block) | |
358 | } | |
359 | ||
360 | fn lower_expr_to_place_without_adjust( | |
361 | &mut self, | |
362 | expr_id: ExprId, | |
363 | place: Place, | |
364 | mut current: BasicBlockId, | |
365 | ) -> Result<Option<BasicBlockId>> { | |
366 | match &self.body.exprs[expr_id] { | |
fe692bf9 FG |
367 | Expr::Missing => { |
368 | if let DefWithBodyId::FunctionId(f) = self.owner { | |
369 | let assoc = self.db.lookup_intern_function(f); | |
370 | if let ItemContainerId::TraitId(t) = assoc.container { | |
371 | let name = &self.db.function_data(f).name; | |
372 | return Err(MirLowerError::TraitFunctionDefinition(t, name.clone())); | |
373 | } | |
374 | } | |
375 | Err(MirLowerError::IncompleteExpr) | |
376 | }, | |
353b0b11 | 377 | Expr::Path(p) => { |
fe692bf9 FG |
378 | let pr = if let Some((assoc, subst)) = self |
379 | .infer | |
380 | .assoc_resolutions_for_expr(expr_id) | |
381 | { | |
382 | match assoc { | |
383 | hir_def::AssocItemId::ConstId(c) => { | |
384 | self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; | |
385 | return Ok(Some(current)) | |
386 | }, | |
387 | hir_def::AssocItemId::FunctionId(_) => { | |
388 | // FnDefs are zero sized, no action is needed. | |
389 | return Ok(Some(current)) | |
353b0b11 | 390 | } |
fe692bf9 FG |
391 | hir_def::AssocItemId::TypeAliasId(_) => { |
392 | // FIXME: If it is unreachable, use proper error instead of `not_supported`. | |
393 | not_supported!("associated functions and types") | |
394 | }, | |
353b0b11 | 395 | } |
fe692bf9 FG |
396 | } else if let Some(variant) = self |
397 | .infer | |
398 | .variant_resolution_for_expr(expr_id) | |
399 | { | |
400 | match variant { | |
401 | VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), | |
402 | VariantId::StructId(s) => ValueNs::StructId(s), | |
403 | VariantId::UnionId(_) => implementation_error!("Union variant as path"), | |
404 | } | |
405 | } else { | |
406 | let unresolved_name = || MirLowerError::unresolved_path(self.db, p); | |
407 | let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); | |
408 | resolver | |
409 | .resolve_path_in_value_ns_fully(self.db.upcast(), p) | |
410 | .ok_or_else(unresolved_name)? | |
353b0b11 FG |
411 | }; |
412 | match pr { | |
fe692bf9 FG |
413 | ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => { |
414 | let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else { | |
415 | return Ok(None); | |
416 | }; | |
353b0b11 FG |
417 | self.push_assignment( |
418 | current, | |
419 | place, | |
fe692bf9 | 420 | Operand::Copy(temp).into(), |
353b0b11 FG |
421 | expr_id.into(), |
422 | ); | |
423 | Ok(Some(current)) | |
424 | } | |
425 | ValueNs::ConstId(const_id) => { | |
fe692bf9 | 426 | self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?; |
353b0b11 FG |
427 | Ok(Some(current)) |
428 | } | |
429 | ValueNs::EnumVariantId(variant_id) => { | |
fe692bf9 FG |
430 | let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; |
431 | if variant_data.variant_data.kind() == StructKind::Unit { | |
432 | let ty = self.infer.type_of_expr[expr_id].clone(); | |
433 | current = self.lower_enum_variant( | |
434 | variant_id, | |
435 | current, | |
436 | place, | |
437 | ty, | |
438 | Box::new([]), | |
439 | expr_id.into(), | |
440 | )?; | |
441 | } | |
442 | // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed | |
353b0b11 FG |
443 | Ok(Some(current)) |
444 | } | |
445 | ValueNs::GenericParam(p) => { | |
446 | let Some(def) = self.owner.as_generic_def_id() else { | |
447 | not_supported!("owner without generic def id"); | |
448 | }; | |
449 | let gen = generics(self.db.upcast(), def); | |
fe692bf9 | 450 | let ty = self.expr_ty_without_adjust(expr_id); |
353b0b11 FG |
451 | self.push_assignment( |
452 | current, | |
453 | place, | |
454 | Operand::Constant( | |
455 | ConstData { | |
456 | ty, | |
457 | value: chalk_ir::ConstValue::BoundVar(BoundVar::new( | |
458 | DebruijnIndex::INNERMOST, | |
459 | gen.param_idx(p.into()).ok_or(MirLowerError::TypeError( | |
460 | "fail to lower const generic param", | |
461 | ))?, | |
462 | )), | |
463 | } | |
464 | .intern(Interner), | |
465 | ) | |
466 | .into(), | |
467 | expr_id.into(), | |
468 | ); | |
469 | Ok(Some(current)) | |
470 | } | |
fe692bf9 | 471 | ValueNs::FunctionId(_) | ValueNs::StructId(_) => { |
353b0b11 FG |
472 | // It's probably a unit struct or a zero sized function, so no action is needed. |
473 | Ok(Some(current)) | |
474 | } | |
475 | x => { | |
476 | not_supported!("unknown name {x:?} in value name space"); | |
477 | } | |
478 | } | |
479 | } | |
480 | Expr::If { condition, then_branch, else_branch } => { | |
481 | let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else { | |
482 | return Ok(None); | |
483 | }; | |
484 | let start_of_then = self.new_basic_block(); | |
485 | let end_of_then = | |
486 | self.lower_expr_to_place(*then_branch, place.clone(), start_of_then)?; | |
487 | let start_of_else = self.new_basic_block(); | |
488 | let end_of_else = if let Some(else_branch) = else_branch { | |
489 | self.lower_expr_to_place(*else_branch, place, start_of_else)? | |
490 | } else { | |
491 | Some(start_of_else) | |
492 | }; | |
493 | self.set_terminator( | |
494 | current, | |
fe692bf9 | 495 | TerminatorKind::SwitchInt { |
353b0b11 FG |
496 | discr, |
497 | targets: SwitchTargets::static_if(1, start_of_then, start_of_else), | |
498 | }, | |
fe692bf9 | 499 | expr_id.into(), |
353b0b11 | 500 | ); |
fe692bf9 | 501 | Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())) |
353b0b11 FG |
502 | } |
503 | Expr::Let { pat, expr } => { | |
504 | let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else { | |
505 | return Ok(None); | |
506 | }; | |
507 | let (then_target, else_target) = self.pattern_match( | |
508 | current, | |
509 | None, | |
510 | cond_place, | |
353b0b11 | 511 | *pat, |
353b0b11 FG |
512 | )?; |
513 | self.write_bytes_to_place( | |
514 | then_target, | |
515 | place.clone(), | |
516 | vec![1], | |
517 | TyBuilder::bool(), | |
518 | MirSpan::Unknown, | |
519 | )?; | |
520 | if let Some(else_target) = else_target { | |
521 | self.write_bytes_to_place( | |
522 | else_target, | |
523 | place, | |
524 | vec![0], | |
525 | TyBuilder::bool(), | |
526 | MirSpan::Unknown, | |
527 | )?; | |
528 | } | |
fe692bf9 | 529 | Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into())) |
353b0b11 FG |
530 | } |
531 | Expr::Unsafe { id: _, statements, tail } => { | |
fe692bf9 | 532 | self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) |
353b0b11 FG |
533 | } |
534 | Expr::Block { id: _, statements, tail, label } => { | |
fe692bf9 FG |
535 | if let Some(label) = label { |
536 | self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| { | |
537 | if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? { | |
538 | let end = this.current_loop_end()?; | |
539 | this.set_goto(current, end, expr_id.into()); | |
540 | } | |
541 | Ok(()) | |
542 | }) | |
543 | } else { | |
544 | self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) | |
545 | } | |
353b0b11 | 546 | } |
fe692bf9 FG |
547 | Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { |
548 | let scope = this.push_drop_scope(); | |
549 | if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { | |
550 | current = scope.pop_and_drop(this, current); | |
551 | this.set_goto(current, begin, expr_id.into()); | |
552 | } else { | |
553 | scope.pop_assume_dropped(this); | |
353b0b11 FG |
554 | } |
555 | Ok(()) | |
556 | }), | |
557 | Expr::While { condition, body, label } => { | |
fe692bf9 FG |
558 | self.lower_loop(current, place, *label, expr_id.into(),|this, begin| { |
559 | let scope = this.push_drop_scope(); | |
353b0b11 FG |
560 | let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { |
561 | return Ok(()); | |
562 | }; | |
fe692bf9 | 563 | let fail_cond = this.new_basic_block(); |
353b0b11 FG |
564 | let after_cond = this.new_basic_block(); |
565 | this.set_terminator( | |
566 | to_switch, | |
fe692bf9 | 567 | TerminatorKind::SwitchInt { |
353b0b11 | 568 | discr, |
fe692bf9 | 569 | targets: SwitchTargets::static_if(1, after_cond, fail_cond), |
353b0b11 | 570 | }, |
fe692bf9 | 571 | expr_id.into(), |
353b0b11 | 572 | ); |
fe692bf9 FG |
573 | let fail_cond = this.drop_until_scope(this.drop_scopes.len() - 1, fail_cond); |
574 | let end = this.current_loop_end()?; | |
575 | this.set_goto(fail_cond, end, expr_id.into()); | |
353b0b11 | 576 | if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { |
fe692bf9 FG |
577 | let block = scope.pop_and_drop(this, block); |
578 | this.set_goto(block, begin, expr_id.into()); | |
579 | } else { | |
580 | scope.pop_assume_dropped(this); | |
353b0b11 FG |
581 | } |
582 | Ok(()) | |
583 | }) | |
584 | } | |
353b0b11 | 585 | Expr::Call { callee, args, .. } => { |
fe692bf9 FG |
586 | if let Some((func_id, generic_args)) = |
587 | self.infer.method_resolution(expr_id) { | |
588 | let ty = chalk_ir::TyKind::FnDef( | |
589 | CallableDefId::FunctionId(func_id).to_chalk(self.db), | |
590 | generic_args, | |
591 | ) | |
592 | .intern(Interner); | |
593 | let func = Operand::from_bytes(vec![], ty); | |
594 | return self.lower_call_and_args( | |
595 | func, | |
596 | iter::once(*callee).chain(args.iter().copied()), | |
597 | place, | |
598 | current, | |
599 | self.is_uninhabited(expr_id), | |
600 | expr_id.into(), | |
601 | ); | |
602 | } | |
353b0b11 FG |
603 | let callee_ty = self.expr_ty_after_adjustments(*callee); |
604 | match &callee_ty.data(Interner).kind { | |
605 | chalk_ir::TyKind::FnDef(..) => { | |
606 | let func = Operand::from_bytes(vec![], callee_ty.clone()); | |
fe692bf9 | 607 | self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) |
353b0b11 | 608 | } |
fe692bf9 FG |
609 | chalk_ir::TyKind::Function(_) => { |
610 | let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else { | |
611 | return Ok(None); | |
612 | }; | |
613 | self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) | |
353b0b11 | 614 | } |
fe692bf9 FG |
615 | TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)), |
616 | _ => return Err(MirLowerError::TypeError("function call on bad type")), | |
353b0b11 FG |
617 | } |
618 | } | |
fe692bf9 | 619 | Expr::MethodCall { receiver, args, method_name, .. } => { |
353b0b11 | 620 | let (func_id, generic_args) = |
fe692bf9 FG |
621 | self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?; |
622 | let func = Operand::from_fn(self.db, func_id, generic_args); | |
353b0b11 FG |
623 | self.lower_call_and_args( |
624 | func, | |
625 | iter::once(*receiver).chain(args.iter().copied()), | |
626 | place, | |
627 | current, | |
628 | self.is_uninhabited(expr_id), | |
fe692bf9 | 629 | expr_id.into(), |
353b0b11 FG |
630 | ) |
631 | } | |
632 | Expr::Match { expr, arms } => { | |
633 | let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)? | |
634 | else { | |
635 | return Ok(None); | |
636 | }; | |
353b0b11 FG |
637 | let mut end = None; |
638 | for MatchArm { pat, guard, expr } in arms.iter() { | |
fe692bf9 | 639 | let (then, mut otherwise) = self.pattern_match( |
353b0b11 FG |
640 | current, |
641 | None, | |
642 | cond_place.clone(), | |
353b0b11 | 643 | *pat, |
353b0b11 | 644 | )?; |
fe692bf9 FG |
645 | let then = if let &Some(guard) = guard { |
646 | let next = self.new_basic_block(); | |
647 | let o = otherwise.get_or_insert_with(|| self.new_basic_block()); | |
648 | if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? { | |
649 | self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into()); | |
650 | } | |
651 | next | |
652 | } else { | |
653 | then | |
654 | }; | |
353b0b11 FG |
655 | if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? { |
656 | let r = end.get_or_insert_with(|| self.new_basic_block()); | |
fe692bf9 | 657 | self.set_goto(block, *r, expr_id.into()); |
353b0b11 FG |
658 | } |
659 | match otherwise { | |
660 | Some(o) => current = o, | |
661 | None => { | |
662 | // The current pattern was irrefutable, so there is no need to generate code | |
663 | // for the rest of patterns | |
664 | break; | |
665 | } | |
666 | } | |
667 | } | |
668 | if self.is_unterminated(current) { | |
fe692bf9 | 669 | self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into()); |
353b0b11 FG |
670 | } |
671 | Ok(end) | |
672 | } | |
fe692bf9 FG |
673 | Expr::Continue { label } => { |
674 | let loop_data = match label { | |
675 | Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?, | |
676 | None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?, | |
677 | }; | |
678 | let begin = loop_data.begin; | |
679 | current = self.drop_until_scope(loop_data.drop_scope_index, current); | |
680 | self.set_goto(current, begin, expr_id.into()); | |
681 | Ok(None) | |
353b0b11 | 682 | }, |
fe692bf9 FG |
683 | &Expr::Break { expr, label } => { |
684 | if let Some(expr) = expr { | |
685 | let loop_data = match label { | |
686 | Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?, | |
687 | None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?, | |
688 | }; | |
689 | let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else { | |
690 | return Ok(None); | |
691 | }; | |
692 | current = c; | |
353b0b11 | 693 | } |
fe692bf9 FG |
694 | let (end, drop_scope) = match label { |
695 | Some(l) => { | |
696 | let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?; | |
697 | (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index) | |
698 | }, | |
353b0b11 | 699 | None => { |
fe692bf9 FG |
700 | (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index) |
701 | }, | |
702 | }; | |
703 | current = self.drop_until_scope(drop_scope, current); | |
704 | self.set_goto(current, end, expr_id.into()); | |
705 | Ok(None) | |
353b0b11 FG |
706 | } |
707 | Expr::Return { expr } => { | |
708 | if let Some(expr) = expr { | |
709 | if let Some(c) = self.lower_expr_to_place(*expr, return_slot().into(), current)? { | |
710 | current = c; | |
711 | } else { | |
712 | return Ok(None); | |
713 | } | |
714 | } | |
fe692bf9 FG |
715 | current = self.drop_until_scope(0, current); |
716 | self.set_terminator(current, TerminatorKind::Return, expr_id.into()); | |
353b0b11 FG |
717 | Ok(None) |
718 | } | |
719 | Expr::Yield { .. } => not_supported!("yield"), | |
fe692bf9 FG |
720 | Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => { |
721 | let spread_place = match spread { | |
722 | &Some(x) => { | |
723 | let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else { | |
724 | return Ok(None); | |
725 | }; | |
726 | current = c; | |
727 | Some(p) | |
728 | }, | |
729 | None => None, | |
730 | }; | |
353b0b11 FG |
731 | let variant_id = self |
732 | .infer | |
733 | .variant_resolution_for_expr(expr_id) | |
734 | .ok_or_else(|| match path { | |
735 | Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()), | |
736 | None => MirLowerError::RecordLiteralWithoutPath, | |
737 | })?; | |
fe692bf9 | 738 | let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) { |
353b0b11 FG |
739 | TyKind::Adt(_, s) => s.clone(), |
740 | _ => not_supported!("Non ADT record literal"), | |
741 | }; | |
742 | let variant_data = variant_id.variant_data(self.db.upcast()); | |
743 | match variant_id { | |
744 | VariantId::EnumVariantId(_) | VariantId::StructId(_) => { | |
745 | let mut operands = vec![None; variant_data.fields().len()]; | |
746 | for RecordLitField { name, expr } in fields.iter() { | |
747 | let field_id = | |
748 | variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; | |
749 | let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else { | |
750 | return Ok(None); | |
751 | }; | |
752 | current = c; | |
753 | operands[u32::from(field_id.into_raw()) as usize] = Some(op); | |
754 | } | |
755 | self.push_assignment( | |
756 | current, | |
757 | place, | |
758 | Rvalue::Aggregate( | |
759 | AggregateKind::Adt(variant_id, subst), | |
fe692bf9 FG |
760 | match spread_place { |
761 | Some(sp) => operands.into_iter().enumerate().map(|(i, x)| { | |
762 | match x { | |
763 | Some(x) => x, | |
764 | None => { | |
765 | let p = sp.project(ProjectionElem::Field(FieldId { | |
766 | parent: variant_id, | |
767 | local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)), | |
768 | })); | |
769 | Operand::Copy(p) | |
770 | }, | |
771 | } | |
772 | }).collect(), | |
773 | None => operands.into_iter().collect::<Option<_>>().ok_or( | |
774 | MirLowerError::TypeError("missing field in record literal"), | |
775 | )?, | |
776 | }, | |
353b0b11 FG |
777 | ), |
778 | expr_id.into(), | |
779 | ); | |
780 | Ok(Some(current)) | |
781 | } | |
782 | VariantId::UnionId(union_id) => { | |
783 | let [RecordLitField { name, expr }] = fields.as_ref() else { | |
784 | not_supported!("Union record literal with more than one field"); | |
785 | }; | |
786 | let local_id = | |
787 | variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; | |
fe692bf9 | 788 | let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id })); |
353b0b11 FG |
789 | self.lower_expr_to_place(*expr, place, current) |
790 | } | |
791 | } | |
792 | } | |
793 | Expr::Await { .. } => not_supported!("await"), | |
353b0b11 | 794 | Expr::Yeet { .. } => not_supported!("yeet"), |
353b0b11 | 795 | Expr::Async { .. } => not_supported!("async block"), |
fe692bf9 FG |
796 | &Expr::Const(id) => { |
797 | let subst = self.placeholder_subst(); | |
798 | self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; | |
799 | Ok(Some(current)) | |
800 | }, | |
353b0b11 FG |
801 | Expr::Cast { expr, type_ref: _ } => { |
802 | let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { | |
803 | return Ok(None); | |
804 | }; | |
805 | let source_ty = self.infer[*expr].clone(); | |
806 | let target_ty = self.infer[expr_id].clone(); | |
807 | self.push_assignment( | |
808 | current, | |
809 | place, | |
810 | Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty), | |
811 | expr_id.into(), | |
812 | ); | |
813 | Ok(Some(current)) | |
814 | } | |
815 | Expr::Ref { expr, rawness: _, mutability } => { | |
816 | let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { | |
817 | return Ok(None); | |
818 | }; | |
819 | let bk = BorrowKind::from_hir(*mutability); | |
820 | self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); | |
821 | Ok(Some(current)) | |
822 | } | |
fe692bf9 FG |
823 | Expr::Box { expr } => { |
824 | let ty = self.expr_ty_after_adjustments(*expr); | |
825 | self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into()); | |
826 | let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { | |
827 | return Ok(None); | |
828 | }; | |
829 | let p = place.project(ProjectionElem::Deref); | |
830 | self.push_assignment(current, p, operand.into(), expr_id.into()); | |
831 | Ok(Some(current)) | |
832 | }, | |
833 | Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => { | |
353b0b11 FG |
834 | let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else { |
835 | return Ok(None); | |
836 | }; | |
837 | self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); | |
838 | Ok(Some(current)) | |
839 | } | |
fe692bf9 | 840 | Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => { |
353b0b11 FG |
841 | let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { |
842 | return Ok(None); | |
843 | }; | |
844 | let operation = match op { | |
fe692bf9 FG |
845 | hir_def::hir::UnaryOp::Not => UnOp::Not, |
846 | hir_def::hir::UnaryOp::Neg => UnOp::Neg, | |
353b0b11 FG |
847 | _ => unreachable!(), |
848 | }; | |
849 | self.push_assignment( | |
850 | current, | |
851 | place, | |
852 | Rvalue::UnaryOp(operation, operand), | |
853 | expr_id.into(), | |
854 | ); | |
855 | Ok(Some(current)) | |
856 | }, | |
857 | Expr::BinaryOp { lhs, rhs, op } => { | |
858 | let op = op.ok_or(MirLowerError::IncompleteExpr)?; | |
fe692bf9 FG |
859 | let is_builtin = 'b: { |
860 | // Without adjust here is a hack. We assume that we know every possible adjustment | |
861 | // for binary operator, and use without adjust to simplify our conditions. | |
862 | let lhs_ty = self.expr_ty_without_adjust(*lhs); | |
863 | let rhs_ty = self.expr_ty_without_adjust(*rhs); | |
864 | if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) { | |
865 | if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() { | |
866 | break 'b true; | |
867 | } | |
353b0b11 | 868 | } |
fe692bf9 FG |
869 | let builtin_inequal_impls = matches!( |
870 | op, | |
871 | BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } | |
872 | ); | |
873 | lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls) | |
874 | }; | |
875 | if !is_builtin { | |
876 | if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { | |
877 | let func = Operand::from_fn(self.db, func_id, generic_args); | |
878 | return self.lower_call_and_args( | |
879 | func, | |
880 | [*lhs, *rhs].into_iter(), | |
881 | place, | |
882 | current, | |
883 | self.is_uninhabited(expr_id), | |
884 | expr_id.into(), | |
885 | ); | |
886 | } | |
887 | } | |
888 | if let hir_def::hir::BinaryOp::Assignment { op } = op { | |
889 | if let Some(op) = op { | |
890 | // last adjustment is `&mut` which we don't want it. | |
891 | let adjusts = self | |
892 | .infer | |
893 | .expr_adjustments | |
894 | .get(lhs) | |
895 | .and_then(|x| x.split_last()) | |
896 | .map(|x| x.1) | |
897 | .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?; | |
898 | let Some((lhs_place, current)) = | |
899 | self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)? | |
900 | else { | |
901 | return Ok(None); | |
902 | }; | |
903 | let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { | |
904 | return Ok(None); | |
905 | }; | |
906 | let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op); | |
907 | self.push_assignment(current, lhs_place, r_value, expr_id.into()); | |
908 | return Ok(Some(current)); | |
909 | } else { | |
910 | let Some((lhs_place, current)) = | |
353b0b11 | 911 | self.lower_expr_as_place(current, *lhs, false)? |
fe692bf9 FG |
912 | else { |
913 | return Ok(None); | |
914 | }; | |
915 | let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { | |
916 | return Ok(None); | |
917 | }; | |
918 | self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); | |
919 | return Ok(Some(current)); | |
920 | } | |
353b0b11 FG |
921 | } |
922 | let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { | |
923 | return Ok(None); | |
924 | }; | |
fe692bf9 FG |
925 | if let hir_def::hir::BinaryOp::LogicOp(op) = op { |
926 | let value_to_short = match op { | |
927 | syntax::ast::LogicOp::And => 0, | |
928 | syntax::ast::LogicOp::Or => 1, | |
929 | }; | |
930 | let start_of_then = self.new_basic_block(); | |
931 | self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into()); | |
932 | let end_of_then = Some(start_of_then); | |
933 | let start_of_else = self.new_basic_block(); | |
934 | let end_of_else = | |
935 | self.lower_expr_to_place(*rhs, place, start_of_else)?; | |
936 | self.set_terminator( | |
937 | current, | |
938 | TerminatorKind::SwitchInt { | |
939 | discr: lhs_op, | |
940 | targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else), | |
941 | }, | |
942 | expr_id.into(), | |
943 | ); | |
944 | return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())); | |
945 | } | |
353b0b11 FG |
946 | let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { |
947 | return Ok(None); | |
948 | }; | |
949 | self.push_assignment( | |
950 | current, | |
951 | place, | |
952 | Rvalue::CheckedBinaryOp( | |
953 | match op { | |
fe692bf9 FG |
954 | hir_def::hir::BinaryOp::LogicOp(op) => match op { |
955 | hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit | |
956 | hir_def::hir::LogicOp::Or => BinOp::BitOr, | |
353b0b11 | 957 | }, |
fe692bf9 FG |
958 | hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op), |
959 | hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op), | |
960 | hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above | |
353b0b11 FG |
961 | }, |
962 | lhs_op, | |
963 | rhs_op, | |
964 | ), | |
965 | expr_id.into(), | |
966 | ); | |
967 | Ok(Some(current)) | |
968 | } | |
fe692bf9 FG |
969 | &Expr::Range { lhs, rhs, range_type: _ } => { |
970 | let ty = self.expr_ty_without_adjust(expr_id); | |
971 | let Some((adt, subst)) = ty.as_adt() else { | |
972 | return Err(MirLowerError::TypeError("Range type is not adt")); | |
973 | }; | |
974 | let AdtId::StructId(st) = adt else { | |
975 | return Err(MirLowerError::TypeError("Range type is not struct")); | |
976 | }; | |
977 | let mut lp = None; | |
978 | let mut rp = None; | |
979 | if let Some(x) = lhs { | |
980 | let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { | |
981 | return Ok(None); | |
982 | }; | |
983 | lp = Some(o); | |
984 | current = c; | |
985 | } | |
986 | if let Some(x) = rhs { | |
987 | let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { | |
988 | return Ok(None); | |
989 | }; | |
990 | rp = Some(o); | |
991 | current = c; | |
992 | } | |
993 | self.push_assignment( | |
994 | current, | |
995 | place, | |
996 | Rvalue::Aggregate( | |
997 | AggregateKind::Adt(st.into(), subst.clone()), | |
998 | self.db.struct_data(st).variant_data.fields().iter().map(|x| { | |
999 | let o = match x.1.name.as_str() { | |
1000 | Some("start") => lp.take(), | |
1001 | Some("end") => rp.take(), | |
1002 | Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())), | |
1003 | _ => None, | |
1004 | }; | |
1005 | o.ok_or(MirLowerError::UnresolvedField) | |
1006 | }).collect::<Result<_>>()?, | |
1007 | ), | |
1008 | expr_id.into(), | |
1009 | ); | |
1010 | Ok(Some(current)) | |
1011 | }, | |
1012 | Expr::Closure { .. } => { | |
1013 | let ty = self.expr_ty_without_adjust(expr_id); | |
1014 | let TyKind::Closure(id, _) = ty.kind(Interner) else { | |
1015 | not_supported!("closure with non closure type"); | |
1016 | }; | |
1017 | self.result.closures.push(*id); | |
1018 | let (captures, _) = self.infer.closure_info(id); | |
1019 | let mut operands = vec![]; | |
1020 | for capture in captures.iter() { | |
1021 | let p = Place { | |
1022 | local: self.binding_local(capture.place.local)?, | |
1023 | projection: capture.place.projections.clone().into_iter().map(|x| { | |
1024 | match x { | |
1025 | ProjectionElem::Deref => ProjectionElem::Deref, | |
1026 | ProjectionElem::Field(x) => ProjectionElem::Field(x), | |
1027 | ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x), | |
1028 | ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end }, | |
1029 | ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, | |
1030 | ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x), | |
1031 | ProjectionElem::Index(x) => match x { }, | |
1032 | } | |
1033 | }).collect(), | |
1034 | }; | |
1035 | match &capture.kind { | |
1036 | CaptureKind::ByRef(bk) => { | |
1037 | let placeholder_subst = self.placeholder_subst(); | |
1038 | let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst); | |
1039 | let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into(); | |
1040 | self.push_assignment( | |
1041 | current, | |
1042 | tmp.clone(), | |
1043 | Rvalue::Ref(bk.clone(), p), | |
1044 | capture.span, | |
1045 | ); | |
1046 | operands.push(Operand::Move(tmp)); | |
1047 | }, | |
1048 | CaptureKind::ByValue => operands.push(Operand::Move(p)), | |
1049 | } | |
1050 | } | |
1051 | self.push_assignment( | |
1052 | current, | |
1053 | place, | |
1054 | Rvalue::Aggregate(AggregateKind::Closure(ty), operands.into()), | |
1055 | expr_id.into(), | |
1056 | ); | |
1057 | Ok(Some(current)) | |
1058 | }, | |
353b0b11 FG |
1059 | Expr::Tuple { exprs, is_assignee_expr: _ } => { |
1060 | let Some(values) = exprs | |
1061 | .iter() | |
1062 | .map(|x| { | |
1063 | let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { | |
1064 | return Ok(None); | |
1065 | }; | |
1066 | current = c; | |
1067 | Ok(Some(o)) | |
1068 | }) | |
1069 | .collect::<Result<Option<_>>>()? | |
1070 | else { | |
1071 | return Ok(None); | |
1072 | }; | |
1073 | let r = Rvalue::Aggregate( | |
fe692bf9 | 1074 | AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)), |
353b0b11 FG |
1075 | values, |
1076 | ); | |
1077 | self.push_assignment(current, place, r, expr_id.into()); | |
1078 | Ok(Some(current)) | |
1079 | } | |
1080 | Expr::Array(l) => match l { | |
1081 | Array::ElementList { elements, .. } => { | |
fe692bf9 | 1082 | let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { |
353b0b11 FG |
1083 | TyKind::Array(ty, _) => ty.clone(), |
1084 | _ => { | |
1085 | return Err(MirLowerError::TypeError( | |
1086 | "Array expression with non array type", | |
1087 | )) | |
1088 | } | |
1089 | }; | |
1090 | let Some(values) = elements | |
1091 | .iter() | |
1092 | .map(|x| { | |
1093 | let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { | |
1094 | return Ok(None); | |
1095 | }; | |
1096 | current = c; | |
1097 | Ok(Some(o)) | |
1098 | }) | |
1099 | .collect::<Result<Option<_>>>()? | |
1100 | else { | |
1101 | return Ok(None); | |
1102 | }; | |
1103 | let r = Rvalue::Aggregate( | |
1104 | AggregateKind::Array(elem_ty), | |
1105 | values, | |
1106 | ); | |
1107 | self.push_assignment(current, place, r, expr_id.into()); | |
1108 | Ok(Some(current)) | |
1109 | } | |
fe692bf9 FG |
1110 | Array::Repeat { initializer, .. } => { |
1111 | let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else { | |
1112 | return Ok(None); | |
1113 | }; | |
1114 | let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { | |
1115 | TyKind::Array(_, len) => len.clone(), | |
1116 | _ => { | |
1117 | return Err(MirLowerError::TypeError( | |
1118 | "Array repeat expression with non array type", | |
1119 | )) | |
1120 | } | |
1121 | }; | |
1122 | let r = Rvalue::Repeat(init, len); | |
1123 | self.push_assignment(current, place, r, expr_id.into()); | |
1124 | Ok(Some(current)) | |
1125 | }, | |
353b0b11 FG |
1126 | }, |
1127 | Expr::Literal(l) => { | |
fe692bf9 | 1128 | let ty = self.expr_ty_without_adjust(expr_id); |
353b0b11 FG |
1129 | let op = self.lower_literal_to_operand(ty, l)?; |
1130 | self.push_assignment(current, place, op.into(), expr_id.into()); | |
1131 | Ok(Some(current)) | |
1132 | } | |
1133 | Expr::Underscore => not_supported!("underscore"), | |
1134 | } | |
1135 | } | |
1136 | ||
fe692bf9 FG |
1137 | fn placeholder_subst(&mut self) -> Substitution { |
1138 | let placeholder_subst = match self.owner.as_generic_def_id() { | |
1139 | Some(x) => TyBuilder::placeholder_subst(self.db, x), | |
1140 | None => Substitution::empty(Interner), | |
1141 | }; | |
1142 | placeholder_subst | |
1143 | } | |
1144 | ||
353b0b11 FG |
1145 | fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> { |
1146 | if let Expr::Field { expr, name } = &self.body[expr_id] { | |
1147 | if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { | |
1148 | let index = name | |
1149 | .as_tuple_index() | |
1150 | .ok_or(MirLowerError::TypeError("named field on tuple"))?; | |
fe692bf9 | 1151 | *place = place.project(ProjectionElem::TupleOrClosureField(index)) |
353b0b11 FG |
1152 | } else { |
1153 | let field = | |
1154 | self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; | |
fe692bf9 | 1155 | *place = place.project(ProjectionElem::Field(field)); |
353b0b11 FG |
1156 | } |
1157 | } else { | |
1158 | not_supported!("") | |
1159 | } | |
1160 | Ok(()) | |
1161 | } | |
1162 | ||
fe692bf9 FG |
1163 | fn lower_literal_or_const_to_operand( |
1164 | &mut self, | |
1165 | ty: Ty, | |
1166 | loc: &LiteralOrConst, | |
1167 | ) -> Result<Operand> { | |
1168 | match loc { | |
1169 | LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l), | |
1170 | LiteralOrConst::Const(c) => { | |
1171 | let unresolved_name = || MirLowerError::unresolved_path(self.db, c); | |
1172 | let resolver = self.owner.resolver(self.db.upcast()); | |
1173 | let pr = resolver | |
1174 | .resolve_path_in_value_ns(self.db.upcast(), c) | |
1175 | .ok_or_else(unresolved_name)?; | |
1176 | match pr { | |
1177 | ResolveValueResult::ValueNs(v) => { | |
1178 | if let ValueNs::ConstId(c) = v { | |
1179 | self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty) | |
1180 | } else { | |
1181 | not_supported!("bad path in range pattern"); | |
1182 | } | |
1183 | } | |
1184 | ResolveValueResult::Partial(_, _) => { | |
1185 | not_supported!("associated constants in range pattern") | |
1186 | } | |
1187 | } | |
1188 | } | |
1189 | } | |
1190 | } | |
1191 | ||
353b0b11 | 1192 | fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> { |
fe692bf9 FG |
1193 | let size = self |
1194 | .db | |
1195 | .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())? | |
353b0b11 FG |
1196 | .size |
1197 | .bytes_usize(); | |
1198 | let bytes = match l { | |
fe692bf9 | 1199 | hir_def::hir::Literal::String(b) => { |
353b0b11 | 1200 | let b = b.as_bytes(); |
fe692bf9 | 1201 | let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2); |
353b0b11 FG |
1202 | data.extend(0usize.to_le_bytes()); |
1203 | data.extend(b.len().to_le_bytes()); | |
1204 | let mut mm = MemoryMap::default(); | |
1205 | mm.insert(0, b.to_vec()); | |
1206 | return Ok(Operand::from_concrete_const(data, mm, ty)); | |
1207 | } | |
fe692bf9 FG |
1208 | hir_def::hir::Literal::CString(b) => { |
1209 | let b = b.as_bytes(); | |
1210 | let bytes = b.iter().copied().chain(iter::once(0)).collect::<Vec<_>>(); | |
1211 | ||
1212 | let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2); | |
1213 | data.extend(0usize.to_le_bytes()); | |
1214 | data.extend(bytes.len().to_le_bytes()); | |
1215 | let mut mm = MemoryMap::default(); | |
1216 | mm.insert(0, bytes); | |
1217 | return Ok(Operand::from_concrete_const(data, mm, ty)); | |
1218 | } | |
1219 | hir_def::hir::Literal::ByteString(b) => { | |
1220 | let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2); | |
353b0b11 FG |
1221 | data.extend(0usize.to_le_bytes()); |
1222 | data.extend(b.len().to_le_bytes()); | |
1223 | let mut mm = MemoryMap::default(); | |
1224 | mm.insert(0, b.to_vec()); | |
1225 | return Ok(Operand::from_concrete_const(data, mm, ty)); | |
1226 | } | |
fe692bf9 FG |
1227 | hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), |
1228 | hir_def::hir::Literal::Bool(b) => vec![*b as u8], | |
1229 | hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(), | |
1230 | hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(), | |
1231 | hir_def::hir::Literal::Float(f, _) => match size { | |
353b0b11 FG |
1232 | 8 => f.into_f64().to_le_bytes().into(), |
1233 | 4 => f.into_f32().to_le_bytes().into(), | |
1234 | _ => { | |
1235 | return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes")) | |
1236 | } | |
1237 | }, | |
1238 | }; | |
1239 | Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty)) | |
1240 | } | |
1241 | ||
1242 | fn new_basic_block(&mut self) -> BasicBlockId { | |
1243 | self.result.basic_blocks.alloc(BasicBlock::default()) | |
1244 | } | |
1245 | ||
1246 | fn lower_const( | |
1247 | &mut self, | |
fe692bf9 | 1248 | const_id: GeneralConstId, |
353b0b11 FG |
1249 | prev_block: BasicBlockId, |
1250 | place: Place, | |
fe692bf9 | 1251 | subst: Substitution, |
353b0b11 | 1252 | span: MirSpan, |
fe692bf9 | 1253 | ty: Ty, |
353b0b11 | 1254 | ) -> Result<()> { |
fe692bf9 FG |
1255 | let c = self.lower_const_to_operand(subst, const_id, ty)?; |
1256 | self.push_assignment(prev_block, place, c.into(), span); | |
1257 | Ok(()) | |
353b0b11 FG |
1258 | } |
1259 | ||
fe692bf9 | 1260 | fn lower_const_to_operand( |
353b0b11 | 1261 | &mut self, |
fe692bf9 FG |
1262 | subst: Substitution, |
1263 | const_id: GeneralConstId, | |
1264 | ty: Ty, | |
1265 | ) -> Result<Operand> { | |
1266 | let c = if subst.len(Interner) != 0 { | |
1267 | // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering. | |
1268 | intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty) | |
1269 | } else { | |
1270 | let name = const_id.name(self.db.upcast()); | |
1271 | self.db | |
1272 | .const_eval(const_id.into(), subst) | |
1273 | .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))? | |
1274 | }; | |
1275 | Ok(Operand::Constant(c)) | |
353b0b11 FG |
1276 | } |
1277 | ||
1278 | fn write_bytes_to_place( | |
1279 | &mut self, | |
1280 | prev_block: BasicBlockId, | |
1281 | place: Place, | |
1282 | cv: Vec<u8>, | |
1283 | ty: Ty, | |
1284 | span: MirSpan, | |
1285 | ) -> Result<()> { | |
1286 | self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span); | |
1287 | Ok(()) | |
1288 | } | |
1289 | ||
1290 | fn lower_enum_variant( | |
1291 | &mut self, | |
1292 | variant_id: EnumVariantId, | |
1293 | prev_block: BasicBlockId, | |
1294 | place: Place, | |
1295 | ty: Ty, | |
fe692bf9 | 1296 | fields: Box<[Operand]>, |
353b0b11 FG |
1297 | span: MirSpan, |
1298 | ) -> Result<BasicBlockId> { | |
1299 | let subst = match ty.kind(Interner) { | |
1300 | TyKind::Adt(_, subst) => subst.clone(), | |
fe692bf9 | 1301 | _ => implementation_error!("Non ADT enum"), |
353b0b11 FG |
1302 | }; |
1303 | self.push_assignment( | |
1304 | prev_block, | |
1305 | place, | |
1306 | Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst), fields), | |
1307 | span, | |
1308 | ); | |
1309 | Ok(prev_block) | |
1310 | } | |
1311 | ||
1312 | fn lower_call_and_args( | |
1313 | &mut self, | |
1314 | func: Operand, | |
1315 | args: impl Iterator<Item = ExprId>, | |
1316 | place: Place, | |
1317 | mut current: BasicBlockId, | |
1318 | is_uninhabited: bool, | |
fe692bf9 | 1319 | span: MirSpan, |
353b0b11 FG |
1320 | ) -> Result<Option<BasicBlockId>> { |
1321 | let Some(args) = args | |
1322 | .map(|arg| { | |
1323 | if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? { | |
1324 | current = c; | |
1325 | Ok(Some(temp)) | |
1326 | } else { | |
1327 | Ok(None) | |
1328 | } | |
1329 | }) | |
1330 | .collect::<Result<Option<Vec<_>>>>()? | |
1331 | else { | |
1332 | return Ok(None); | |
1333 | }; | |
fe692bf9 | 1334 | self.lower_call(func, args.into(), place, current, is_uninhabited, span) |
353b0b11 FG |
1335 | } |
1336 | ||
1337 | fn lower_call( | |
1338 | &mut self, | |
1339 | func: Operand, | |
fe692bf9 | 1340 | args: Box<[Operand]>, |
353b0b11 FG |
1341 | place: Place, |
1342 | current: BasicBlockId, | |
1343 | is_uninhabited: bool, | |
fe692bf9 | 1344 | span: MirSpan, |
353b0b11 FG |
1345 | ) -> Result<Option<BasicBlockId>> { |
1346 | let b = if is_uninhabited { None } else { Some(self.new_basic_block()) }; | |
1347 | self.set_terminator( | |
1348 | current, | |
fe692bf9 | 1349 | TerminatorKind::Call { |
353b0b11 FG |
1350 | func, |
1351 | args, | |
1352 | destination: place, | |
1353 | target: b, | |
1354 | cleanup: None, | |
1355 | from_hir_call: true, | |
1356 | }, | |
fe692bf9 | 1357 | span, |
353b0b11 FG |
1358 | ); |
1359 | Ok(b) | |
1360 | } | |
1361 | ||
1362 | fn is_unterminated(&mut self, source: BasicBlockId) -> bool { | |
1363 | self.result.basic_blocks[source].terminator.is_none() | |
1364 | } | |
1365 | ||
fe692bf9 FG |
1366 | fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) { |
1367 | self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator }); | |
353b0b11 FG |
1368 | } |
1369 | ||
fe692bf9 FG |
1370 | fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) { |
1371 | self.set_terminator(source, TerminatorKind::Goto { target }, span); | |
353b0b11 FG |
1372 | } |
1373 | ||
fe692bf9 | 1374 | fn expr_ty_without_adjust(&self, e: ExprId) -> Ty { |
353b0b11 FG |
1375 | self.infer[e].clone() |
1376 | } | |
1377 | ||
1378 | fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { | |
1379 | let mut ty = None; | |
1380 | if let Some(x) = self.infer.expr_adjustments.get(&e) { | |
1381 | if let Some(x) = x.last() { | |
1382 | ty = Some(x.target.clone()); | |
1383 | } | |
1384 | } | |
fe692bf9 | 1385 | ty.unwrap_or_else(|| self.expr_ty_without_adjust(e)) |
353b0b11 FG |
1386 | } |
1387 | ||
1388 | fn push_statement(&mut self, block: BasicBlockId, statement: Statement) { | |
1389 | self.result.basic_blocks[block].statements.push(statement); | |
1390 | } | |
1391 | ||
1392 | fn push_assignment( | |
1393 | &mut self, | |
1394 | block: BasicBlockId, | |
1395 | place: Place, | |
1396 | rvalue: Rvalue, | |
1397 | span: MirSpan, | |
1398 | ) { | |
1399 | self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); | |
1400 | } | |
1401 | ||
fe692bf9 | 1402 | fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { |
353b0b11 FG |
1403 | match &self.discr_temp { |
1404 | Some(x) => x.clone(), | |
1405 | None => { | |
fe692bf9 FG |
1406 | let tmp: Place = self |
1407 | .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown) | |
1408 | .expect("discr_ty is never unsized") | |
1409 | .into(); | |
353b0b11 FG |
1410 | self.discr_temp = Some(tmp.clone()); |
1411 | tmp | |
1412 | } | |
1413 | } | |
1414 | } | |
1415 | ||
1416 | fn lower_loop( | |
1417 | &mut self, | |
1418 | prev_block: BasicBlockId, | |
fe692bf9 | 1419 | place: Place, |
353b0b11 | 1420 | label: Option<LabelId>, |
fe692bf9 | 1421 | span: MirSpan, |
353b0b11 FG |
1422 | f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>, |
1423 | ) -> Result<Option<BasicBlockId>> { | |
353b0b11 | 1424 | let begin = self.new_basic_block(); |
fe692bf9 FG |
1425 | let prev = mem::replace( |
1426 | &mut self.current_loop_blocks, | |
1427 | Some(LoopBlocks { begin, end: None, place, drop_scope_index: self.drop_scopes.len() }), | |
1428 | ); | |
1429 | let prev_label = if let Some(label) = label { | |
1430 | // We should generate the end now, to make sure that it wouldn't change later. It is | |
1431 | // bad as we may emit end (unnecessary unreachable block) for unterminating loop, but | |
1432 | // it should not affect correctness. | |
1433 | self.current_loop_end()?; | |
1434 | self.labeled_loop_blocks | |
1435 | .insert(label, self.current_loop_blocks.as_ref().unwrap().clone()) | |
1436 | } else { | |
1437 | None | |
1438 | }; | |
1439 | self.set_goto(prev_block, begin, span); | |
353b0b11 | 1440 | f(self, begin)?; |
fe692bf9 FG |
1441 | let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or( |
1442 | MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()), | |
1443 | )?; | |
1444 | if let Some(prev) = prev_label { | |
1445 | self.labeled_loop_blocks.insert(label.unwrap(), prev); | |
1446 | } | |
353b0b11 FG |
1447 | Ok(my.end) |
1448 | } | |
1449 | ||
1450 | fn has_adjustments(&self, expr_id: ExprId) -> bool { | |
1451 | !self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true) | |
1452 | } | |
1453 | ||
1454 | fn merge_blocks( | |
1455 | &mut self, | |
1456 | b1: Option<BasicBlockId>, | |
1457 | b2: Option<BasicBlockId>, | |
fe692bf9 | 1458 | span: MirSpan, |
353b0b11 FG |
1459 | ) -> Option<BasicBlockId> { |
1460 | match (b1, b2) { | |
1461 | (None, None) => None, | |
1462 | (None, Some(b)) | (Some(b), None) => Some(b), | |
1463 | (Some(b1), Some(b2)) => { | |
1464 | let bm = self.new_basic_block(); | |
fe692bf9 FG |
1465 | self.set_goto(b1, bm, span); |
1466 | self.set_goto(b2, bm, span); | |
353b0b11 FG |
1467 | Some(bm) |
1468 | } | |
1469 | } | |
1470 | } | |
1471 | ||
1472 | fn current_loop_end(&mut self) -> Result<BasicBlockId> { | |
1473 | let r = match self | |
1474 | .current_loop_blocks | |
1475 | .as_mut() | |
fe692bf9 FG |
1476 | .ok_or(MirLowerError::ImplementationError( |
1477 | "Current loop access out of loop".to_string(), | |
1478 | ))? | |
353b0b11 FG |
1479 | .end |
1480 | { | |
1481 | Some(x) => x, | |
1482 | None => { | |
1483 | let s = self.new_basic_block(); | |
1484 | self.current_loop_blocks | |
1485 | .as_mut() | |
fe692bf9 FG |
1486 | .ok_or(MirLowerError::ImplementationError( |
1487 | "Current loop access out of loop".to_string(), | |
1488 | ))? | |
353b0b11 FG |
1489 | .end = Some(s); |
1490 | s | |
1491 | } | |
1492 | }; | |
1493 | Ok(r) | |
1494 | } | |
1495 | ||
1496 | fn is_uninhabited(&self, expr_id: ExprId) -> bool { | |
1497 | is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db) | |
1498 | } | |
1499 | ||
fe692bf9 FG |
1500 | /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and |
1501 | /// `Drop` in the appropriated places. | |
1502 | fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> { | |
353b0b11 FG |
1503 | let span = self.body.bindings[b] |
1504 | .definitions | |
1505 | .first() | |
1506 | .copied() | |
1507 | .map(MirSpan::PatId) | |
1508 | .unwrap_or(MirSpan::Unknown); | |
fe692bf9 FG |
1509 | let l = self.binding_local(b)?; |
1510 | self.push_storage_live_for_local(l, current, span) | |
1511 | } | |
1512 | ||
1513 | fn push_storage_live_for_local( | |
1514 | &mut self, | |
1515 | l: LocalId, | |
1516 | current: BasicBlockId, | |
1517 | span: MirSpan, | |
1518 | ) -> Result<()> { | |
1519 | self.drop_scopes.last_mut().unwrap().locals.push(l); | |
353b0b11 | 1520 | self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); |
fe692bf9 | 1521 | Ok(()) |
353b0b11 FG |
1522 | } |
1523 | ||
1524 | fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> { | |
1525 | let crate_id = self.owner.module(self.db.upcast()).krate(); | |
1526 | self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item)) | |
1527 | } | |
1528 | ||
1529 | fn lower_block_to_place( | |
1530 | &mut self, | |
fe692bf9 | 1531 | statements: &[hir_def::hir::Statement], |
353b0b11 FG |
1532 | mut current: BasicBlockId, |
1533 | tail: Option<ExprId>, | |
1534 | place: Place, | |
fe692bf9 | 1535 | span: MirSpan, |
353b0b11 | 1536 | ) -> Result<Option<Idx<BasicBlock>>> { |
fe692bf9 | 1537 | let scope = self.push_drop_scope(); |
353b0b11 FG |
1538 | for statement in statements.iter() { |
1539 | match statement { | |
fe692bf9 | 1540 | hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => { |
353b0b11 FG |
1541 | if let Some(expr_id) = initializer { |
1542 | let else_block; | |
1543 | let Some((init_place, c)) = | |
1544 | self.lower_expr_as_place(current, *expr_id, true)? | |
1545 | else { | |
fe692bf9 | 1546 | scope.pop_assume_dropped(self); |
353b0b11 FG |
1547 | return Ok(None); |
1548 | }; | |
1549 | current = c; | |
fe692bf9 FG |
1550 | (current, else_block) = |
1551 | self.pattern_match(current, None, init_place, *pat)?; | |
353b0b11 FG |
1552 | match (else_block, else_branch) { |
1553 | (None, _) => (), | |
1554 | (Some(else_block), None) => { | |
fe692bf9 | 1555 | self.set_terminator(else_block, TerminatorKind::Unreachable, span); |
353b0b11 FG |
1556 | } |
1557 | (Some(else_block), Some(else_branch)) => { | |
1558 | if let Some((_, b)) = | |
1559 | self.lower_expr_as_place(else_block, *else_branch, true)? | |
1560 | { | |
fe692bf9 | 1561 | self.set_terminator(b, TerminatorKind::Unreachable, span); |
353b0b11 FG |
1562 | } |
1563 | } | |
1564 | } | |
1565 | } else { | |
fe692bf9 | 1566 | let mut err = None; |
353b0b11 | 1567 | self.body.walk_bindings_in_pat(*pat, |b| { |
fe692bf9 FG |
1568 | if let Err(e) = self.push_storage_live(b, current) { |
1569 | err = Some(e); | |
1570 | } | |
353b0b11 | 1571 | }); |
fe692bf9 FG |
1572 | if let Some(e) = err { |
1573 | return Err(e); | |
1574 | } | |
353b0b11 FG |
1575 | } |
1576 | } | |
fe692bf9 FG |
1577 | hir_def::hir::Statement::Expr { expr, has_semi: _ } => { |
1578 | let scope2 = self.push_drop_scope(); | |
353b0b11 | 1579 | let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { |
fe692bf9 FG |
1580 | scope2.pop_assume_dropped(self); |
1581 | scope.pop_assume_dropped(self); | |
353b0b11 FG |
1582 | return Ok(None); |
1583 | }; | |
fe692bf9 | 1584 | current = scope2.pop_and_drop(self, c); |
353b0b11 FG |
1585 | } |
1586 | } | |
1587 | } | |
fe692bf9 FG |
1588 | if let Some(tail) = tail { |
1589 | let Some(c) = self.lower_expr_to_place(tail, place, current)? else { | |
1590 | scope.pop_assume_dropped(self); | |
1591 | return Ok(None); | |
1592 | }; | |
1593 | current = c; | |
353b0b11 | 1594 | } |
fe692bf9 FG |
1595 | current = scope.pop_and_drop(self, current); |
1596 | Ok(Some(current)) | |
353b0b11 | 1597 | } |
353b0b11 | 1598 | |
fe692bf9 FG |
1599 | fn lower_params_and_bindings( |
1600 | &mut self, | |
1601 | params: impl Iterator<Item = (PatId, Ty)> + Clone, | |
1602 | pick_binding: impl Fn(BindingId) -> bool, | |
1603 | ) -> Result<BasicBlockId> { | |
1604 | let base_param_count = self.result.param_locals.len(); | |
1605 | self.result.param_locals.extend(params.clone().map(|(x, ty)| { | |
1606 | let local_id = self.result.locals.alloc(Local { ty }); | |
1607 | self.drop_scopes.last_mut().unwrap().locals.push(local_id); | |
1608 | if let Pat::Bind { id, subpat: None } = self.body[x] { | |
1609 | if matches!( | |
1610 | self.body.bindings[id].mode, | |
1611 | BindingAnnotation::Unannotated | BindingAnnotation::Mutable | |
1612 | ) { | |
1613 | self.result.binding_locals.insert(id, local_id); | |
1614 | } | |
1615 | } | |
1616 | local_id | |
1617 | })); | |
1618 | // and then rest of bindings | |
1619 | for (id, _) in self.body.bindings.iter() { | |
1620 | if !pick_binding(id) { | |
1621 | continue; | |
1622 | } | |
1623 | if !self.result.binding_locals.contains_idx(id) { | |
1624 | self.result | |
1625 | .binding_locals | |
1626 | .insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() })); | |
1627 | } | |
1628 | } | |
1629 | let mut current = self.result.start_block; | |
1630 | for ((param, _), local) in | |
1631 | params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count)) | |
1632 | { | |
1633 | if let Pat::Bind { id, .. } = self.body[param] { | |
1634 | if local == self.binding_local(id)? { | |
1635 | continue; | |
1636 | } | |
1637 | } | |
1638 | let r = self.pattern_match(current, None, local.into(), param)?; | |
1639 | if let Some(b) = r.1 { | |
1640 | self.set_terminator(b, TerminatorKind::Unreachable, param.into()); | |
1641 | } | |
1642 | current = r.0; | |
1643 | } | |
1644 | Ok(current) | |
1645 | } | |
1646 | ||
1647 | fn binding_local(&self, b: BindingId) -> Result<LocalId> { | |
1648 | match self.result.binding_locals.get(b) { | |
1649 | Some(x) => Ok(*x), | |
1650 | None => { | |
1651 | // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which | |
1652 | // is a hir lowering problem IMO. | |
1653 | // never!("Using unaccessable local for binding is always a bug"); | |
1654 | Err(MirLowerError::UnaccessableLocal) | |
1655 | } | |
1656 | } | |
1657 | } | |
1658 | ||
1659 | fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> { | |
1660 | let r = self.db.const_eval_discriminant(variant); | |
1661 | match r { | |
1662 | Ok(r) => Ok(r), | |
1663 | Err(e) => { | |
1664 | let data = self.db.enum_data(variant.parent); | |
1665 | let name = format!( | |
1666 | "{}::{}", | |
1667 | data.name.display(self.db.upcast()), | |
1668 | data.variants[variant.local_id].name.display(self.db.upcast()) | |
1669 | ); | |
1670 | Err(MirLowerError::ConstEvalError(name, Box::new(e))) | |
1671 | } | |
1672 | } | |
1673 | } | |
1674 | ||
1675 | fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId { | |
1676 | for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() { | |
1677 | self.emit_drop_and_storage_dead_for_scope(scope, &mut current); | |
1678 | } | |
1679 | current | |
1680 | } | |
1681 | ||
1682 | fn push_drop_scope(&mut self) -> DropScopeToken { | |
1683 | self.drop_scopes.push(DropScope::default()); | |
1684 | DropScopeToken | |
1685 | } | |
1686 | ||
1687 | /// Don't call directly | |
1688 | fn pop_drop_scope_assume_dropped_internal(&mut self) { | |
1689 | self.drop_scopes.pop(); | |
1690 | } | |
1691 | ||
1692 | /// Don't call directly | |
1693 | fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId { | |
1694 | let scope = self.drop_scopes.pop().unwrap(); | |
1695 | self.emit_drop_and_storage_dead_for_scope(&scope, &mut current); | |
1696 | current | |
1697 | } | |
1698 | ||
1699 | fn pop_drop_scope_assert_finished( | |
1700 | &mut self, | |
1701 | mut current: BasicBlockId, | |
1702 | ) -> Result<BasicBlockId> { | |
1703 | current = self.pop_drop_scope_internal(current); | |
1704 | if !self.drop_scopes.is_empty() { | |
1705 | implementation_error!("Mismatched count between drop scope push and pops"); | |
1706 | } | |
1707 | Ok(current) | |
1708 | } | |
1709 | ||
1710 | fn emit_drop_and_storage_dead_for_scope( | |
1711 | &mut self, | |
1712 | scope: &DropScope, | |
1713 | current: &mut Idx<BasicBlock>, | |
1714 | ) { | |
1715 | for &l in scope.locals.iter().rev() { | |
1716 | if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) { | |
1717 | let prev = std::mem::replace(current, self.new_basic_block()); | |
1718 | self.set_terminator( | |
1719 | prev, | |
1720 | TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, | |
1721 | MirSpan::Unknown, | |
1722 | ); | |
1723 | } | |
1724 | self.push_statement( | |
1725 | *current, | |
1726 | StatementKind::StorageDead(l).with_span(MirSpan::Unknown), | |
1727 | ); | |
353b0b11 | 1728 | } |
353b0b11 FG |
1729 | } |
1730 | } | |
1731 | ||
1732 | fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> { | |
1733 | Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) { | |
1734 | (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) { | |
1735 | (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat, | |
1736 | (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt, | |
1737 | (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, | |
1738 | (_, _) => CastKind::IntToInt, | |
1739 | }, | |
fe692bf9 FG |
1740 | (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress, |
1741 | (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress, | |
1742 | (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { | |
1743 | CastKind::Pointer(if a == b { | |
1744 | PointerCast::MutToConstPointer | |
1745 | } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str) | |
1746 | && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str) | |
1747 | { | |
1748 | // slice to slice cast is no-op (metadata is not touched), so we use this | |
1749 | PointerCast::MutToConstPointer | |
1750 | } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { | |
1751 | PointerCast::Unsize | |
1752 | } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { | |
1753 | PointerCast::ArrayToPointer | |
1754 | } else { | |
1755 | // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant | |
1756 | // for it in `PointerCast` so we use `MutToConstPointer` | |
1757 | PointerCast::MutToConstPointer | |
1758 | }) | |
1759 | } | |
353b0b11 FG |
1760 | // Enum to int casts |
1761 | (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { | |
1762 | CastKind::IntToInt | |
1763 | } | |
1764 | (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"), | |
1765 | }) | |
1766 | } | |
1767 | ||
fe692bf9 FG |
1768 | pub fn mir_body_for_closure_query( |
1769 | db: &dyn HirDatabase, | |
1770 | closure: ClosureId, | |
1771 | ) -> Result<Arc<MirBody>> { | |
1772 | let (owner, expr) = db.lookup_intern_closure(closure.into()); | |
1773 | let body = db.body(owner); | |
1774 | let infer = db.infer(owner); | |
1775 | let Expr::Closure { args, body: root, .. } = &body[expr] else { | |
1776 | implementation_error!("closure expression is not closure"); | |
1777 | }; | |
1778 | let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else { | |
1779 | implementation_error!("closure expression is not closure"); | |
1780 | }; | |
1781 | let (captures, kind) = infer.closure_info(&closure); | |
1782 | let mut ctx = MirLowerCtx::new(db, owner, &body, &infer); | |
1783 | // 0 is return local | |
1784 | ctx.result.locals.alloc(Local { ty: infer[*root].clone() }); | |
1785 | let closure_local = ctx.result.locals.alloc(Local { | |
1786 | ty: match kind { | |
1787 | FnTrait::FnOnce => infer[expr].clone(), | |
1788 | FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone()) | |
1789 | .intern(Interner), | |
1790 | FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone()) | |
1791 | .intern(Interner), | |
1792 | }, | |
1793 | }); | |
1794 | ctx.result.param_locals.push(closure_local); | |
1795 | let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else { | |
1796 | implementation_error!("closure has not callable sig"); | |
1797 | }; | |
1798 | let current = ctx.lower_params_and_bindings( | |
1799 | args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())), | |
1800 | |_| true, | |
1801 | )?; | |
1802 | if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { | |
1803 | let current = ctx.pop_drop_scope_assert_finished(current)?; | |
1804 | ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); | |
1805 | } | |
1806 | let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default(); | |
1807 | for (i, capture) in captures.iter().enumerate() { | |
1808 | let local = ctx.binding_local(capture.place.local)?; | |
1809 | upvar_map.entry(local).or_default().push((capture, i)); | |
1810 | } | |
1811 | let mut err = None; | |
1812 | let closure_local = ctx.result.locals.iter().nth(1).unwrap().0; | |
1813 | let closure_projection = match kind { | |
1814 | FnTrait::FnOnce => vec![], | |
1815 | FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref], | |
1816 | }; | |
1817 | ctx.result.walk_places(|p| { | |
1818 | if let Some(x) = upvar_map.get(&p.local) { | |
1819 | let r = x.iter().find(|x| { | |
1820 | if p.projection.len() < x.0.place.projections.len() { | |
1821 | return false; | |
1822 | } | |
1823 | for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) { | |
1824 | match (x, y) { | |
1825 | (ProjectionElem::Deref, ProjectionElem::Deref) => (), | |
1826 | (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (), | |
1827 | ( | |
1828 | ProjectionElem::TupleOrClosureField(x), | |
1829 | ProjectionElem::TupleOrClosureField(y), | |
1830 | ) if x == y => (), | |
1831 | _ => return false, | |
1832 | } | |
1833 | } | |
1834 | true | |
1835 | }); | |
1836 | match r { | |
1837 | Some(x) => { | |
1838 | p.local = closure_local; | |
1839 | let mut next_projs = closure_projection.clone(); | |
1840 | next_projs.push(PlaceElem::TupleOrClosureField(x.1)); | |
1841 | let prev_projs = mem::take(&mut p.projection); | |
1842 | if x.0.kind != CaptureKind::ByValue { | |
1843 | next_projs.push(ProjectionElem::Deref); | |
1844 | } | |
1845 | next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len())); | |
1846 | p.projection = next_projs.into(); | |
1847 | } | |
1848 | None => err = Some(p.clone()), | |
1849 | } | |
1850 | } | |
1851 | }); | |
1852 | ctx.result.binding_locals = ctx | |
1853 | .result | |
1854 | .binding_locals | |
1855 | .into_iter() | |
1856 | .filter(|it| ctx.body.binding_owners.get(&it.0).copied() == Some(expr)) | |
1857 | .collect(); | |
1858 | if let Some(err) = err { | |
1859 | return Err(MirLowerError::UnresolvedUpvar(err)); | |
1860 | } | |
1861 | ctx.result.shrink_to_fit(); | |
1862 | Ok(Arc::new(ctx.result)) | |
1863 | } | |
1864 | ||
353b0b11 FG |
1865 | pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> { |
1866 | let _p = profile::span("mir_body_query").detail(|| match def { | |
fe692bf9 FG |
1867 | DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(), |
1868 | DefWithBodyId::StaticId(it) => db.static_data(it).name.display(db.upcast()).to_string(), | |
1869 | DefWithBodyId::ConstId(it) => db | |
1870 | .const_data(it) | |
1871 | .name | |
1872 | .clone() | |
1873 | .unwrap_or_else(Name::missing) | |
1874 | .display(db.upcast()) | |
1875 | .to_string(), | |
353b0b11 | 1876 | DefWithBodyId::VariantId(it) => { |
fe692bf9 | 1877 | db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string() |
353b0b11 | 1878 | } |
fe692bf9 | 1879 | DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"), |
353b0b11 FG |
1880 | }); |
1881 | let body = db.body(def); | |
1882 | let infer = db.infer(def); | |
fe692bf9 FG |
1883 | let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?; |
1884 | result.shrink_to_fit(); | |
353b0b11 FG |
1885 | Ok(Arc::new(result)) |
1886 | } | |
1887 | ||
1888 | pub fn mir_body_recover( | |
1889 | _db: &dyn HirDatabase, | |
1890 | _cycle: &[String], | |
1891 | _def: &DefWithBodyId, | |
1892 | ) -> Result<Arc<MirBody>> { | |
1893 | Err(MirLowerError::Loop) | |
1894 | } | |
1895 | ||
1896 | pub fn lower_to_mir( | |
1897 | db: &dyn HirDatabase, | |
1898 | owner: DefWithBodyId, | |
1899 | body: &Body, | |
1900 | infer: &InferenceResult, | |
1901 | // FIXME: root_expr should always be the body.body_expr, but since `X` in `[(); X]` doesn't have its own specific body yet, we | |
1902 | // need to take this input explicitly. | |
1903 | root_expr: ExprId, | |
1904 | ) -> Result<MirBody> { | |
1905 | if let Some((_, x)) = infer.type_mismatches().next() { | |
1906 | return Err(MirLowerError::TypeMismatch(x.clone())); | |
1907 | } | |
fe692bf9 | 1908 | let mut ctx = MirLowerCtx::new(db, owner, body, infer); |
353b0b11 | 1909 | // 0 is return local |
fe692bf9 FG |
1910 | ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) }); |
1911 | let binding_picker = |b: BindingId| { | |
1912 | let owner = ctx.body.binding_owners.get(&b).copied(); | |
1913 | if root_expr == body.body_expr { | |
1914 | owner.is_none() | |
1915 | } else { | |
1916 | owner == Some(root_expr) | |
353b0b11 | 1917 | } |
353b0b11 | 1918 | }; |
fe692bf9 FG |
1919 | // 1 to param_len is for params |
1920 | // FIXME: replace with let chain once it becomes stable | |
1921 | let current = 'b: { | |
1922 | if body.body_expr == root_expr { | |
1923 | // otherwise it's an inline const, and has no parameter | |
1924 | if let DefWithBodyId::FunctionId(fid) = owner { | |
1925 | let substs = TyBuilder::placeholder_subst(db, fid); | |
1926 | let callable_sig = | |
1927 | db.callable_item_signature(fid.into()).substitute(Interner, &substs); | |
1928 | break 'b ctx.lower_params_and_bindings( | |
1929 | body.params | |
1930 | .iter() | |
1931 | .zip(callable_sig.params().iter()) | |
1932 | .map(|(x, y)| (*x, y.clone())), | |
1933 | binding_picker, | |
1934 | )?; | |
353b0b11 FG |
1935 | } |
1936 | } | |
fe692bf9 FG |
1937 | ctx.lower_params_and_bindings([].into_iter(), binding_picker)? |
1938 | }; | |
1939 | if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { | |
1940 | let current = ctx.pop_drop_scope_assert_finished(current)?; | |
1941 | ctx.set_terminator(current, TerminatorKind::Return, root_expr.into()); | |
353b0b11 FG |
1942 | } |
1943 | Ok(ctx.result) | |
1944 | } |