Fix compiler panic on stack with infinite spacing (#3918)

This commit is contained in:
Malo 2024-04-18 14:20:05 +02:00 committed by Laurenz
parent 03c30556db
commit ac7dff10b2
4 changed files with 48 additions and 10 deletions

View File

@ -2692,6 +2692,10 @@ impl<'a> GridLayouter<'a> {
height: Abs, height: Abs,
y: usize, y: usize,
) -> SourceResult<Frame> { ) -> SourceResult<Frame> {
if !self.width.is_finite() {
bail!(self.span, "cannot create grid with infinite width");
}
if !height.is_finite() { if !height.is_finite() {
bail!(self.span, "cannot create grid with infinite height"); bail!(self.span, "cannot create grid with infinite height");
} }

View File

@ -1,6 +1,7 @@
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use typst_syntax::Span;
use crate::diag::SourceResult; use crate::diag::{bail, SourceResult};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{cast, elem, Content, Packed, Resolve, StyleChain, StyledElem}; use crate::foundations::{cast, elem, Content, Packed, Resolve, StyleChain, StyledElem};
use crate::layout::{ use crate::layout::{
@ -59,7 +60,8 @@ impl LayoutMultiple for Packed<StackElem> {
styles: StyleChain, styles: StyleChain,
regions: Regions, regions: Regions,
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
let mut layouter = StackLayouter::new(self.dir(styles), regions, styles); let mut layouter =
StackLayouter::new(self.span(), self.dir(styles), regions, styles);
let axis = layouter.dir.axis(); let axis = layouter.dir.axis();
// Spacing to insert before the next block. // Spacing to insert before the next block.
@ -97,7 +99,7 @@ impl LayoutMultiple for Packed<StackElem> {
} }
} }
Ok(layouter.finish()) layouter.finish()
} }
} }
@ -131,6 +133,8 @@ cast! {
/// Performs stack layout. /// Performs stack layout.
struct StackLayouter<'a> { struct StackLayouter<'a> {
/// The span to raise errors at during layout.
span: Span,
/// The stacking direction. /// The stacking direction.
dir: Dir, dir: Dir,
/// The axis of the stacking direction. /// The axis of the stacking direction.
@ -166,7 +170,12 @@ enum StackItem {
impl<'a> StackLayouter<'a> { impl<'a> StackLayouter<'a> {
/// Create a new stack layouter. /// Create a new stack layouter.
fn new(dir: Dir, mut regions: Regions<'a>, styles: StyleChain<'a>) -> Self { fn new(
span: Span,
dir: Dir,
mut regions: Regions<'a>,
styles: StyleChain<'a>,
) -> Self {
let axis = dir.axis(); let axis = dir.axis();
let expand = regions.expand; let expand = regions.expand;
@ -174,6 +183,7 @@ impl<'a> StackLayouter<'a> {
regions.expand.set(axis, false); regions.expand.set(axis, false);
Self { Self {
span,
dir, dir,
axis, axis,
regions, regions,
@ -218,7 +228,7 @@ impl<'a> StackLayouter<'a> {
styles: StyleChain, styles: StyleChain,
) -> SourceResult<()> { ) -> SourceResult<()> {
if self.regions.is_full() { if self.regions.is_full() {
self.finish_region(); self.finish_region()?;
} }
// Block-axis alignment of the `AlignElement` is respected by stacks. // Block-axis alignment of the `AlignElement` is respected by stacks.
@ -251,7 +261,7 @@ impl<'a> StackLayouter<'a> {
self.items.push(StackItem::Frame(frame, align)); self.items.push(StackItem::Frame(frame, align));
if i + 1 < len { if i + 1 < len {
self.finish_region(); self.finish_region()?;
} }
} }
@ -259,7 +269,7 @@ impl<'a> StackLayouter<'a> {
} }
/// Advance to the next region. /// Advance to the next region.
fn finish_region(&mut self) { fn finish_region(&mut self) -> SourceResult<()> {
// Determine the size of the stack in this region depending on whether // Determine the size of the stack in this region depending on whether
// the region expands. // the region expands.
let mut size = self let mut size = self
@ -275,6 +285,10 @@ impl<'a> StackLayouter<'a> {
size.set(self.axis, full); size.set(self.axis, full);
} }
if !size.is_finite() {
bail!(self.span, "stack spacing is infinite");
}
let mut output = Frame::hard(size); let mut output = Frame::hard(size);
let mut cursor = Abs::zero(); let mut cursor = Abs::zero();
let mut ruler: FixedAlignment = self.dir.start().into(); let mut ruler: FixedAlignment = self.dir.start().into();
@ -320,12 +334,14 @@ impl<'a> StackLayouter<'a> {
self.used = Gen::zero(); self.used = Gen::zero();
self.fr = Fr::zero(); self.fr = Fr::zero();
self.finished.push(output); self.finished.push(output);
Ok(())
} }
/// Finish layouting and return the resulting frames. /// Finish layouting and return the resulting frames.
fn finish(mut self) -> Fragment { fn finish(mut self) -> SourceResult<Fragment> {
self.finish_region(); self.finish_region()?;
Fragment::frames(self.finished) Ok(Fragment::frames(self.finished))
} }
} }

View File

@ -274,3 +274,12 @@ The following:
[], [],
[a] [a]
) )
--- issue-3917-grid-with-infinite-width ---
// https://github.com/typst/typst/issues/1918
#set page(width: auto)
#context layout(available => {
let infinite-length = available.width
// Error: 3-50 cannot create grid with infinite width
grid(gutter: infinite-length, columns: 2)[A][B]
})

View File

@ -80,3 +80,12 @@ World! 🌍
stack([a], 1fr, [b]), stack([a], 1fr, [b]),
stack([a], v(1fr), [b]), stack([a], v(1fr), [b]),
) )
--- issue-1918-stack-with-infinite-spacing ---
// https://github.com/typst/typst/issues/1918
#set page(width: auto)
#context layout(available => {
let infinite-length = available.width
// Error: 3-40 stack spacing is infinite
stack(spacing: infinite-length)[A][B]
})