1 use crate::arena::{Arena, Handle, UniqueArena};
2 
3 use super::{Error, LookupExpression, LookupHelper as _};
4 
5 #[derive(Clone, Debug)]
6 pub(super) struct LookupSampledImage {
7     image: Handle<crate::Expression>,
8     sampler: Handle<crate::Expression>,
9 }
10 
11 bitflags::bitflags! {
12     /// Flags describing sampling method.
13     pub struct SamplingFlags: u32 {
14         /// Regular sampling.
15         const REGULAR = 0x1;
16         /// Comparison sampling.
17         const COMPARISON = 0x2;
18     }
19 }
20 
21 impl<'function> super::BlockContext<'function> {
get_image_expr_ty( &self, handle: Handle<crate::Expression>, ) -> Result<Handle<crate::Type>, Error>22     fn get_image_expr_ty(
23         &self,
24         handle: Handle<crate::Expression>,
25     ) -> Result<Handle<crate::Type>, Error> {
26         match self.expressions[handle] {
27             crate::Expression::GlobalVariable(handle) => Ok(self.global_arena[handle].ty),
28             crate::Expression::FunctionArgument(i) => Ok(self.arguments[i as usize].ty),
29             ref other => Err(Error::InvalidImageExpression(other.clone())),
30         }
31     }
32 }
33 
34 /// Options of a sampling operation.
35 #[derive(Debug)]
36 pub struct SamplingOptions {
37     /// Projection sampling: the division by W is expected to happen
38     /// in the texture unit.
39     pub project: bool,
40     /// Depth comparison sampling with a reference value.
41     pub compare: bool,
42 }
43 
44 enum ExtraCoordinate {
45     ArrayLayer,
46     Projection,
47     Garbage,
48 }
49 
50 /// Return the texture coordinates separated from the array layer,
51 /// and/or divided by the projection term.
52 ///
53 /// The Proj sampling ops expect an extra coordinate for the W.
54 /// The arrayed (can't be Proj!) images expect an extra coordinate for the layer.
extract_image_coordinates( image_dim: crate::ImageDimension, extra_coordinate: ExtraCoordinate, base: Handle<crate::Expression>, coordinate_ty: Handle<crate::Type>, ctx: &mut super::BlockContext, ) -> (Handle<crate::Expression>, Option<Handle<crate::Expression>>)55 fn extract_image_coordinates(
56     image_dim: crate::ImageDimension,
57     extra_coordinate: ExtraCoordinate,
58     base: Handle<crate::Expression>,
59     coordinate_ty: Handle<crate::Type>,
60     ctx: &mut super::BlockContext,
61 ) -> (Handle<crate::Expression>, Option<Handle<crate::Expression>>) {
62     let (given_size, kind) = match ctx.type_arena[coordinate_ty].inner {
63         crate::TypeInner::Scalar { kind, .. } => (None, kind),
64         crate::TypeInner::Vector { size, kind, .. } => (Some(size), kind),
65         ref other => unreachable!("Unexpected texture coordinate {:?}", other),
66     };
67 
68     let required_size = image_dim.required_coordinate_size();
69     let required_ty = required_size.map(|size| {
70         ctx.type_arena
71             .get(&crate::Type {
72                 name: None,
73                 inner: crate::TypeInner::Vector {
74                     size,
75                     kind,
76                     width: 4,
77                 },
78             })
79             .expect("Required coordinate type should have been set up by `parse_type_image`!")
80     });
81     let extra_expr = crate::Expression::AccessIndex {
82         base,
83         index: required_size.map_or(1, |size| size as u32),
84     };
85 
86     let base_span = ctx.expressions.get_span(base);
87 
88     match extra_coordinate {
89         ExtraCoordinate::ArrayLayer => {
90             let extracted = match required_size {
91                 None => ctx
92                     .expressions
93                     .append(crate::Expression::AccessIndex { base, index: 0 }, base_span),
94                 Some(size) => {
95                     let mut components = Vec::with_capacity(size as usize);
96                     for index in 0..size as u32 {
97                         let comp = ctx
98                             .expressions
99                             .append(crate::Expression::AccessIndex { base, index }, base_span);
100                         components.push(comp);
101                     }
102                     ctx.expressions.append(
103                         crate::Expression::Compose {
104                             ty: required_ty.unwrap(),
105                             components,
106                         },
107                         base_span,
108                     )
109                 }
110             };
111             let array_index_f32 = ctx.expressions.append(extra_expr, base_span);
112             let array_index = ctx.expressions.append(
113                 crate::Expression::As {
114                     kind: crate::ScalarKind::Sint,
115                     expr: array_index_f32,
116                     convert: Some(4),
117                 },
118                 base_span,
119             );
120             (extracted, Some(array_index))
121         }
122         ExtraCoordinate::Projection => {
123             let projection = ctx.expressions.append(extra_expr, base_span);
124             let divided = match required_size {
125                 None => {
126                     let temp = ctx
127                         .expressions
128                         .append(crate::Expression::AccessIndex { base, index: 0 }, base_span);
129                     ctx.expressions.append(
130                         crate::Expression::Binary {
131                             op: crate::BinaryOperator::Divide,
132                             left: temp,
133                             right: projection,
134                         },
135                         base_span,
136                     )
137                 }
138                 Some(size) => {
139                     let mut components = Vec::with_capacity(size as usize);
140                     for index in 0..size as u32 {
141                         let temp = ctx
142                             .expressions
143                             .append(crate::Expression::AccessIndex { base, index }, base_span);
144                         let comp = ctx.expressions.append(
145                             crate::Expression::Binary {
146                                 op: crate::BinaryOperator::Divide,
147                                 left: temp,
148                                 right: projection,
149                             },
150                             base_span,
151                         );
152                         components.push(comp);
153                     }
154                     ctx.expressions.append(
155                         crate::Expression::Compose {
156                             ty: required_ty.unwrap(),
157                             components,
158                         },
159                         base_span,
160                     )
161                 }
162             };
163             (divided, None)
164         }
165         ExtraCoordinate::Garbage if given_size == required_size => (base, None),
166         ExtraCoordinate::Garbage => {
167             use crate::SwizzleComponent as Sc;
168             let cut_expr = match required_size {
169                 None => crate::Expression::AccessIndex { base, index: 0 },
170                 Some(size) => crate::Expression::Swizzle {
171                     size,
172                     vector: base,
173                     pattern: [Sc::X, Sc::Y, Sc::Z, Sc::W],
174                 },
175             };
176             (ctx.expressions.append(cut_expr, base_span), None)
177         }
178     }
179 }
180 
patch_comparison_type( flags: SamplingFlags, var: &mut crate::GlobalVariable, arena: &mut UniqueArena<crate::Type>, ) -> bool181 pub(super) fn patch_comparison_type(
182     flags: SamplingFlags,
183     var: &mut crate::GlobalVariable,
184     arena: &mut UniqueArena<crate::Type>,
185 ) -> bool {
186     if !flags.contains(SamplingFlags::COMPARISON) {
187         return true;
188     }
189     if flags == SamplingFlags::all() {
190         return false;
191     }
192 
193     log::debug!("Flipping comparison for {:?}", var);
194     let original_ty = &arena[var.ty];
195     let original_ty_span = arena.get_span(var.ty);
196     let ty_inner = match original_ty.inner {
197         crate::TypeInner::Image {
198             class: crate::ImageClass::Sampled { multi, .. },
199             dim,
200             arrayed,
201         } => crate::TypeInner::Image {
202             class: crate::ImageClass::Depth { multi },
203             dim,
204             arrayed,
205         },
206         crate::TypeInner::Sampler { .. } => crate::TypeInner::Sampler { comparison: true },
207         ref other => unreachable!("Unexpected type for comparison mutation: {:?}", other),
208     };
209 
210     let name = original_ty.name.clone();
211     var.ty = arena.insert(
212         crate::Type {
213             name,
214             inner: ty_inner,
215         },
216         original_ty_span,
217     );
218     true
219 }
220 
221 impl<I: Iterator<Item = u32>> super::Parser<I> {
parse_image_couple(&mut self) -> Result<(), Error>222     pub(super) fn parse_image_couple(&mut self) -> Result<(), Error> {
223         let _result_type_id = self.next()?;
224         let result_id = self.next()?;
225         let image_id = self.next()?;
226         let sampler_id = self.next()?;
227         let image_lexp = self.lookup_expression.lookup(image_id)?;
228         let sampler_lexp = self.lookup_expression.lookup(sampler_id)?;
229         self.lookup_sampled_image.insert(
230             result_id,
231             LookupSampledImage {
232                 image: image_lexp.handle,
233                 sampler: sampler_lexp.handle,
234             },
235         );
236         Ok(())
237     }
238 
parse_image_uncouple(&mut self, block_id: spirv::Word) -> Result<(), Error>239     pub(super) fn parse_image_uncouple(&mut self, block_id: spirv::Word) -> Result<(), Error> {
240         let result_type_id = self.next()?;
241         let result_id = self.next()?;
242         let sampled_image_id = self.next()?;
243         self.lookup_expression.insert(
244             result_id,
245             LookupExpression {
246                 handle: self.lookup_sampled_image.lookup(sampled_image_id)?.image,
247                 type_id: result_type_id,
248                 block_id,
249             },
250         );
251         Ok(())
252     }
253 
parse_image_write( &mut self, words_left: u16, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, body_idx: usize, ) -> Result<crate::Statement, Error>254     pub(super) fn parse_image_write(
255         &mut self,
256         words_left: u16,
257         ctx: &mut super::BlockContext,
258         emitter: &mut crate::front::Emitter,
259         block: &mut crate::Block,
260         body_idx: usize,
261     ) -> Result<crate::Statement, Error> {
262         let image_id = self.next()?;
263         let coordinate_id = self.next()?;
264         let value_id = self.next()?;
265 
266         let image_ops = if words_left != 0 { self.next()? } else { 0 };
267 
268         if image_ops != 0 {
269             let other = spirv::ImageOperands::from_bits_truncate(image_ops);
270             log::warn!("Unknown image write ops {:?}", other);
271             for _ in 1..words_left {
272                 self.next()?;
273             }
274         }
275 
276         let image_lexp = self.lookup_expression.lookup(image_id)?;
277         let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
278 
279         let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
280         let coord_handle =
281             self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
282         let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
283         let (coordinate, array_index) = match ctx.type_arena[image_ty].inner {
284             crate::TypeInner::Image {
285                 dim,
286                 arrayed,
287                 class: _,
288             } => extract_image_coordinates(
289                 dim,
290                 if arrayed {
291                     ExtraCoordinate::ArrayLayer
292                 } else {
293                     ExtraCoordinate::Garbage
294                 },
295                 coord_handle,
296                 coord_type_handle,
297                 ctx,
298             ),
299             _ => return Err(Error::InvalidImage(image_ty)),
300         };
301 
302         let value_lexp = self.lookup_expression.lookup(value_id)?;
303         let value = self.get_expr_handle(value_id, value_lexp, ctx, emitter, block, body_idx);
304 
305         Ok(crate::Statement::ImageStore {
306             image: image_lexp.handle,
307             coordinate,
308             array_index,
309             value,
310         })
311     }
312 
parse_image_load( &mut self, mut words_left: u16, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, block_id: spirv::Word, body_idx: usize, ) -> Result<(), Error>313     pub(super) fn parse_image_load(
314         &mut self,
315         mut words_left: u16,
316         ctx: &mut super::BlockContext,
317         emitter: &mut crate::front::Emitter,
318         block: &mut crate::Block,
319         block_id: spirv::Word,
320         body_idx: usize,
321     ) -> Result<(), Error> {
322         let start = self.data_offset;
323         let result_type_id = self.next()?;
324         let result_id = self.next()?;
325         let image_id = self.next()?;
326         let coordinate_id = self.next()?;
327 
328         let mut image_ops = if words_left != 0 {
329             words_left -= 1;
330             self.next()?
331         } else {
332             0
333         };
334 
335         let mut index = None;
336         while image_ops != 0 {
337             let bit = 1 << image_ops.trailing_zeros();
338             match spirv::ImageOperands::from_bits_truncate(bit) {
339                 spirv::ImageOperands::LOD => {
340                     let lod_expr = self.next()?;
341                     let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
342                     let lod_handle =
343                         self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
344                     index = Some(lod_handle);
345                     words_left -= 1;
346                 }
347                 spirv::ImageOperands::SAMPLE => {
348                     let sample_expr = self.next()?;
349                     let sample_handle = self.lookup_expression.lookup(sample_expr)?.handle;
350                     index = Some(sample_handle);
351                     words_left -= 1;
352                 }
353                 other => {
354                     log::warn!("Unknown image load op {:?}", other);
355                     for _ in 0..words_left {
356                         self.next()?;
357                     }
358                     break;
359                 }
360             }
361             image_ops ^= bit;
362         }
363 
364         // No need to call get_expr_handle here since only globals/arguments are
365         // allowed as images and they are always in the root scope
366         let image_lexp = self.lookup_expression.lookup(image_id)?;
367         let image_ty = ctx.get_image_expr_ty(image_lexp.handle)?;
368 
369         let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
370         let coord_handle =
371             self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
372         let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
373         let (coordinate, array_index) = match ctx.type_arena[image_ty].inner {
374             crate::TypeInner::Image {
375                 dim,
376                 arrayed,
377                 class: _,
378             } => extract_image_coordinates(
379                 dim,
380                 if arrayed {
381                     ExtraCoordinate::ArrayLayer
382                 } else {
383                     ExtraCoordinate::Garbage
384                 },
385                 coord_handle,
386                 coord_type_handle,
387                 ctx,
388             ),
389             _ => return Err(Error::InvalidImage(image_ty)),
390         };
391 
392         let expr = crate::Expression::ImageLoad {
393             image: image_lexp.handle,
394             coordinate,
395             array_index,
396             index,
397         };
398         self.lookup_expression.insert(
399             result_id,
400             LookupExpression {
401                 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
402                 type_id: result_type_id,
403                 block_id,
404             },
405         );
406         Ok(())
407     }
408 
409     #[allow(clippy::too_many_arguments)]
parse_image_sample( &mut self, mut words_left: u16, options: SamplingOptions, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, block_id: spirv::Word, body_idx: usize, ) -> Result<(), Error>410     pub(super) fn parse_image_sample(
411         &mut self,
412         mut words_left: u16,
413         options: SamplingOptions,
414         ctx: &mut super::BlockContext,
415         emitter: &mut crate::front::Emitter,
416         block: &mut crate::Block,
417         block_id: spirv::Word,
418         body_idx: usize,
419     ) -> Result<(), Error> {
420         let start = self.data_offset;
421         let result_type_id = self.next()?;
422         let result_id = self.next()?;
423         let sampled_image_id = self.next()?;
424         let coordinate_id = self.next()?;
425         let dref_id = if options.compare {
426             Some(self.next()?)
427         } else {
428             None
429         };
430 
431         let mut image_ops = if words_left != 0 {
432             words_left -= 1;
433             self.next()?
434         } else {
435             0
436         };
437 
438         let mut level = crate::SampleLevel::Auto;
439         let mut offset = None;
440         while image_ops != 0 {
441             let bit = 1 << image_ops.trailing_zeros();
442             match spirv::ImageOperands::from_bits_truncate(bit) {
443                 spirv::ImageOperands::BIAS => {
444                     let bias_expr = self.next()?;
445                     let bias_lexp = self.lookup_expression.lookup(bias_expr)?;
446                     let bias_handle =
447                         self.get_expr_handle(bias_expr, bias_lexp, ctx, emitter, block, body_idx);
448                     level = crate::SampleLevel::Bias(bias_handle);
449                     words_left -= 1;
450                 }
451                 spirv::ImageOperands::LOD => {
452                     let lod_expr = self.next()?;
453                     let lod_lexp = self.lookup_expression.lookup(lod_expr)?;
454                     let lod_handle =
455                         self.get_expr_handle(lod_expr, lod_lexp, ctx, emitter, block, body_idx);
456                     level = if options.compare {
457                         log::debug!("Assuming {:?} is zero", lod_handle);
458                         crate::SampleLevel::Zero
459                     } else {
460                         crate::SampleLevel::Exact(lod_handle)
461                     };
462                     words_left -= 1;
463                 }
464                 spirv::ImageOperands::GRAD => {
465                     let grad_x_expr = self.next()?;
466                     let grad_x_lexp = self.lookup_expression.lookup(grad_x_expr)?;
467                     let grad_x_handle = self.get_expr_handle(
468                         grad_x_expr,
469                         grad_x_lexp,
470                         ctx,
471                         emitter,
472                         block,
473                         body_idx,
474                     );
475                     let grad_y_expr = self.next()?;
476                     let grad_y_lexp = self.lookup_expression.lookup(grad_y_expr)?;
477                     let grad_y_handle = self.get_expr_handle(
478                         grad_y_expr,
479                         grad_y_lexp,
480                         ctx,
481                         emitter,
482                         block,
483                         body_idx,
484                     );
485                     level = if options.compare {
486                         log::debug!(
487                             "Assuming gradients {:?} and {:?} are not greater than 1",
488                             grad_x_handle,
489                             grad_y_handle
490                         );
491                         crate::SampleLevel::Zero
492                     } else {
493                         crate::SampleLevel::Gradient {
494                             x: grad_x_handle,
495                             y: grad_y_handle,
496                         }
497                     };
498                     words_left -= 2;
499                 }
500                 spirv::ImageOperands::CONST_OFFSET => {
501                     let offset_constant = self.next()?;
502                     let offset_handle = self.lookup_constant.lookup(offset_constant)?.handle;
503                     offset = Some(offset_handle);
504                     words_left -= 1;
505                 }
506                 other => {
507                     log::warn!("Unknown image sample operand {:?}", other);
508                     for _ in 0..words_left {
509                         self.next()?;
510                     }
511                     break;
512                 }
513             }
514             image_ops ^= bit;
515         }
516 
517         let si_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;
518         let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
519         let coord_handle =
520             self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
521         let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
522 
523         let sampling_bit = if options.compare {
524             SamplingFlags::COMPARISON
525         } else {
526             SamplingFlags::REGULAR
527         };
528 
529         let image_ty = match ctx.expressions[si_lexp.image] {
530             crate::Expression::GlobalVariable(handle) => {
531                 if let Some(flags) = self.handle_sampling.get_mut(&handle) {
532                     *flags |= sampling_bit;
533                 }
534 
535                 ctx.global_arena[handle].ty
536             }
537             crate::Expression::FunctionArgument(i) => {
538                 ctx.parameter_sampling[i as usize] |= sampling_bit;
539                 ctx.arguments[i as usize].ty
540             }
541             ref other => return Err(Error::InvalidGlobalVar(other.clone())),
542         };
543         match ctx.expressions[si_lexp.sampler] {
544             crate::Expression::GlobalVariable(handle) => {
545                 *self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit
546             }
547             crate::Expression::FunctionArgument(i) => {
548                 ctx.parameter_sampling[i as usize] |= sampling_bit;
549             }
550             ref other => return Err(Error::InvalidGlobalVar(other.clone())),
551         }
552 
553         let ((coordinate, array_index), depth_ref) = match ctx.type_arena[image_ty].inner {
554             crate::TypeInner::Image {
555                 dim,
556                 arrayed,
557                 class: _,
558             } => (
559                 extract_image_coordinates(
560                     dim,
561                     if options.project {
562                         ExtraCoordinate::Projection
563                     } else if arrayed {
564                         ExtraCoordinate::ArrayLayer
565                     } else {
566                         ExtraCoordinate::Garbage
567                     },
568                     coord_handle,
569                     coord_type_handle,
570                     ctx,
571                 ),
572                 {
573                     match dref_id {
574                         Some(id) => {
575                             let expr_lexp = self.lookup_expression.lookup(id)?;
576                             let mut expr =
577                                 self.get_expr_handle(id, expr_lexp, ctx, emitter, block, body_idx);
578 
579                             if options.project {
580                                 let required_size = dim.required_coordinate_size();
581                                 let right = ctx.expressions.append(
582                                     crate::Expression::AccessIndex {
583                                         base: coord_handle,
584                                         index: required_size.map_or(1, |size| size as u32),
585                                     },
586                                     crate::Span::default(),
587                                 );
588                                 expr = ctx.expressions.append(
589                                     crate::Expression::Binary {
590                                         op: crate::BinaryOperator::Divide,
591                                         left: expr,
592                                         right,
593                                     },
594                                     crate::Span::default(),
595                                 )
596                             };
597                             Some(expr)
598                         }
599                         None => None,
600                     }
601                 },
602             ),
603             _ => return Err(Error::InvalidImage(image_ty)),
604         };
605 
606         let expr = crate::Expression::ImageSample {
607             image: si_lexp.image,
608             sampler: si_lexp.sampler,
609             gather: None, //TODO
610             coordinate,
611             array_index,
612             offset,
613             level,
614             depth_ref,
615         };
616         self.lookup_expression.insert(
617             result_id,
618             LookupExpression {
619                 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
620                 type_id: result_type_id,
621                 block_id,
622             },
623         );
624         Ok(())
625     }
626 
parse_image_query_size( &mut self, at_level: bool, ctx: &mut super::BlockContext, emitter: &mut crate::front::Emitter, block: &mut crate::Block, block_id: spirv::Word, body_idx: usize, ) -> Result<(), Error>627     pub(super) fn parse_image_query_size(
628         &mut self,
629         at_level: bool,
630         ctx: &mut super::BlockContext,
631         emitter: &mut crate::front::Emitter,
632         block: &mut crate::Block,
633         block_id: spirv::Word,
634         body_idx: usize,
635     ) -> Result<(), Error> {
636         let start = self.data_offset;
637         let result_type_id = self.next()?;
638         let result_id = self.next()?;
639         let image_id = self.next()?;
640         let level = if at_level {
641             let level_id = self.next()?;
642             let level_lexp = self.lookup_expression.lookup(level_id)?;
643             Some(self.get_expr_handle(level_id, level_lexp, ctx, emitter, block, body_idx))
644         } else {
645             None
646         };
647 
648         // No need to call get_expr_handle here since only globals/arguments are
649         // allowed as images and they are always in the root scope
650         //TODO: handle arrays and cubes
651         let image_lexp = self.lookup_expression.lookup(image_id)?;
652 
653         let expr = crate::Expression::ImageQuery {
654             image: image_lexp.handle,
655             query: crate::ImageQuery::Size { level },
656         };
657         self.lookup_expression.insert(
658             result_id,
659             LookupExpression {
660                 handle: ctx.expressions.append(expr, self.span_from_with_op(start)),
661                 type_id: result_type_id,
662                 block_id,
663             },
664         );
665         Ok(())
666     }
667 
parse_image_query_other( &mut self, query: crate::ImageQuery, expressions: &mut Arena<crate::Expression>, block_id: spirv::Word, ) -> Result<(), Error>668     pub(super) fn parse_image_query_other(
669         &mut self,
670         query: crate::ImageQuery,
671         expressions: &mut Arena<crate::Expression>,
672         block_id: spirv::Word,
673     ) -> Result<(), Error> {
674         let start = self.data_offset;
675         let result_type_id = self.next()?;
676         let result_id = self.next()?;
677         let image_id = self.next()?;
678 
679         // No need to call get_expr_handle here since only globals/arguments are
680         // allowed as images and they are always in the root scope
681         let image_lexp = self.lookup_expression.lookup(image_id)?.clone();
682 
683         let expr = crate::Expression::ImageQuery {
684             image: image_lexp.handle,
685             query,
686         };
687         self.lookup_expression.insert(
688             result_id,
689             LookupExpression {
690                 handle: expressions.append(expr, self.span_from_with_op(start)),
691                 type_id: result_type_id,
692                 block_id,
693             },
694         );
695         Ok(())
696     }
697 }
698