Merge pull request #5374 from GabrielDertoni/remove-needless-string-alloc

refactor: remove needless string allocation in pretty printer
This commit is contained in:
Folkert de Vries 2023-05-06 00:34:48 +02:00 committed by GitHub
commit ec21f19826
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 99 deletions

View file

@ -9,7 +9,7 @@ use crate::pattern::{Pattern, RecordDestruct, TupleDestruct};
use roc_module::symbol::{Interns, ModuleId, Symbol};
use ven_pretty::{Arena, DocAllocator, DocBuilder};
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
pub struct Ctx<'a> {
pub home: ModuleId,
@ -163,10 +163,10 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
use EPrec::*;
match e {
Num(_, n, _, _) | Int(_, _, n, _, _) | Float(_, _, n, _, _) => f.text(&**n),
Str(s) => f.text(format!(r#""{}""#, s)),
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
Str(s) => text!(f, r#""{}""#, s),
SingleQuote(_, _, c, _) => text!(f, "'{}'", c),
IngestedFile(file_path, bytes, _) => {
f.text(format!("<ingested {:?}, {} bytes>", file_path, bytes.len()))
text!(f, "<ingested {:?}, {} bytes>", file_path, bytes.len())
}
List {
elem_var: _,
@ -354,15 +354,15 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
RecordAccess {
loc_expr, field, ..
} => expr(c, AppArg, f, &loc_expr.value)
.append(f.text(format!(".{}", field.as_str())))
.append(text!(f, ".{}", field.as_str()))
.group(),
TupleAccess {
loc_expr, index, ..
} => expr(c, AppArg, f, &loc_expr.value)
.append(f.text(format!(".{index}")))
.append(text!(f, ".{index}"))
.group(),
OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => {
f.text(format!("@{}", opaque_name.as_str(c.interns)))
text!(f, "@{}", opaque_name.as_str(c.interns))
}
RecordAccessor(_) => todo!(),
RecordUpdate {
@ -436,11 +436,12 @@ fn pp_sym<'a>(c: &Ctx, f: &'a Arena<'a>, sym: Symbol) -> DocBuilder<'a, Arena<'a
if sym.module_id() == c.home {
f.text(sym.as_str(c.interns).to_owned())
} else {
f.text(format!(
text!(
f,
"{}.{}",
sym.module_string(c.interns),
sym.as_str(c.interns),
))
)
}
}
@ -516,8 +517,7 @@ fn pattern<'a>(
),
UnwrappedOpaque {
opaque, argument, ..
} => f
.text(format!("@{} ", opaque.module_string(c.interns)))
} => text!(f, "@{} ", opaque.module_string(c.interns))
.append(pattern(c, Free, f, &argument.1.value))
.group(),
RecordDestructure { destructs, .. } => f
@ -559,8 +559,8 @@ fn pattern<'a>(
NumLiteral(_, n, _, _) | IntLiteral(_, _, n, _, _) | FloatLiteral(_, _, n, _, _) => {
f.text(&**n)
}
StrLiteral(s) => f.text(format!(r#""{}""#, s)),
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
StrLiteral(s) => text!(f, r#""{}""#, s),
SingleQuote(_, _, c, _) => text!(f, "'{}'", c),
Underscore => f.text("_"),
Shadowed(_, _, _) => todo!(),

View file

@ -1,7 +1,7 @@
use std::fmt::Display;
use roc_module::symbol::{Interns, Symbol};
use ven_pretty::{Arena, DocAllocator, DocBuilder};
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
use crate::{
ir::{Parens, ProcLayout},
@ -103,11 +103,7 @@ fn format_sourced_doc<'d>(f: &'d Arena<'d>, line: usize, source: &str, doc: Doc<
fn format_header<'d>(f: &'d Arena<'d>, title: &str) -> Doc<'d> {
let title_width = title.len() + 4;
f.text(format!(
"── {} {}",
title,
"".repeat(HEADER_WIDTH - title_width)
))
text!(f, "── {} {}", title, "".repeat(HEADER_WIDTH - title_width))
}
fn format_kind<'a, 'd, I>(

View file

@ -36,7 +36,7 @@ use roc_types::subs::{
StorageSubs, Subs, Variable, VariableSubsSlice,
};
use std::collections::HashMap;
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
use ven_pretty::{text, BoxAllocator, DocAllocator, DocBuilder};
use pattern::{from_can_pattern, store_pattern, Pattern};
@ -1647,7 +1647,7 @@ impl ModifyRc {
.append(";"),
Inc(symbol, n) => alloc
.text("inc ")
.append(alloc.text(format!("{} ", n)))
.append(text!(alloc, "{} ", n))
.append(symbol_to_doc(alloc, symbol, pretty))
.append(";"),
Dec(symbol) => alloc
@ -1700,24 +1700,19 @@ impl<'a> Call<'a> {
LowLevel { op: lowlevel, .. } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc
.text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " "))
text!(alloc, "lowlevel {:?} ", lowlevel).append(alloc.intersperse(it, " "))
}
HigherOrder(higher_order) => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc
.text(format!("lowlevel {:?} ", higher_order.op))
.append(alloc.intersperse(it, " "))
text!(alloc, "lowlevel {:?} ", higher_order.op).append(alloc.intersperse(it, " "))
}
Foreign {
ref foreign_symbol, ..
} => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc
.text(format!("foreign {:?} ", foreign_symbol.as_str()))
text!(alloc, "foreign {:?} ", foreign_symbol.as_str())
.append(alloc.intersperse(it, " "))
}
}
@ -1920,13 +1915,13 @@ impl<'a> Literal<'a> {
use Literal::*;
match self {
Int(bytes) => alloc.text(format!("{}i64", i128::from_ne_bytes(*bytes))),
U128(bytes) => alloc.text(format!("{}u128", u128::from_ne_bytes(*bytes))),
Float(lit) => alloc.text(format!("{}f64", lit)),
Decimal(bytes) => alloc.text(format!("{}dec", RocDec::from_ne_bytes(*bytes))),
Bool(lit) => alloc.text(format!("{}", lit)),
Byte(lit) => alloc.text(format!("{}u8", lit)),
Str(lit) => alloc.text(format!("{:?}", lit)),
Int(bytes) => text!(alloc, "{}i64", i128::from_ne_bytes(*bytes)),
U128(bytes) => text!(alloc, "{}u128", u128::from_ne_bytes(*bytes)),
Float(lit) => text!(alloc, "{}f64", lit),
Decimal(bytes) => text!(alloc, "{}dec", RocDec::from_ne_bytes(*bytes)),
Bool(lit) => text!(alloc, "{}", lit),
Byte(lit) => text!(alloc, "{}u8", lit),
Str(lit) => text!(alloc, "{:?}", lit),
}
}
}
@ -2064,11 +2059,10 @@ impl<'a> Expr<'a> {
StructAtIndex {
index, structure, ..
} => alloc
.text(format!("StructAtIndex {} ", index))
} => text!(alloc, "StructAtIndex {} ", index)
.append(symbol_to_doc(alloc, *structure, pretty)),
RuntimeErrorFunction(s) => alloc.text(format!("ErrorFunction {}", s)),
RuntimeErrorFunction(s) => text!(alloc, "ErrorFunction {}", s),
GetTagId { structure, .. } => alloc
.text("GetTagId ")
@ -2087,8 +2081,7 @@ impl<'a> Expr<'a> {
structure,
index,
..
} => alloc
.text(format!("UnionAtIndex (Id {}) (Index {}) ", tag_id, index))
} => text!(alloc, "UnionAtIndex (Id {}) (Index {}) ", tag_id, index)
.append(symbol_to_doc(alloc, *structure, pretty)),
}
}
@ -2216,8 +2209,7 @@ impl<'a> Stmt<'a> {
let branches_docs = branches
.iter()
.map(|(tag, _info, expr)| {
alloc
.text(format!("case {}:", tag))
text!(alloc, "case {}:", tag)
.append(alloc.hardline())
.append(expr.to_doc(alloc, interner, pretty).indent(4))
.indent(4)

View file

@ -1343,7 +1343,7 @@ mod debug_types {
use super::{TypeTag, Types};
use roc_collections::soa::{Index, Slice};
use roc_module::ident::TagName;
use ven_pretty::{Arena, DocAllocator, DocBuilder};
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
pub struct DebugTag<'a>(pub &'a Types, pub Index<TypeTag>);
@ -1409,13 +1409,13 @@ mod debug_types {
f.text("[")
.append(
f.intersperse(
Some(f.text(format!("{name:?}")))
Some(text!(f, "{name:?}"))
.into_iter()
.chain(captures.into_iter().map(|c| typ(types, f, Free, c))),
f.text(" "),
),
)
.append(f.text(format!(", ^{ambient_function:?}")))
.append(text!(f, ", ^{ambient_function:?}"))
.append(f.text("]"))
}
TypeTag::FunctionOrTagUnion(_, _) => {
@ -1426,7 +1426,7 @@ mod debug_types {
unspecialized: Uls(var, sym, region),
} => f
.text("[")
.append(f.text(format!("{var:?}:{sym:?}:{region}")))
.append(text!(f, "{var:?}:{sym:?}:{region}"))
.append(f.text("]")),
TypeTag::DelayedAlias { shared } => {
maybe_paren!(Free, p, alias(types, f, tag, shared))
@ -1464,7 +1464,7 @@ mod debug_types {
)
)
}
TypeTag::Variable(var) => f.text(format!("{var:?}")),
TypeTag::Variable(var) => text!(f, "{var:?}"),
TypeTag::RangedNumber(range) => ranged(f, range),
TypeTag::Error => f.text("ERROR"),
TypeTag::TagUnion(tags, _) => {
@ -1473,7 +1473,7 @@ mod debug_types {
TypeTag::RecursiveTagUnion(rec, tags, _) => tag_union(
types,
f,
f.text(format!("<rec {rec:?}>")),
text!(f, "<rec {rec:?}>"),
tags,
types.get_type_arguments(tag),
),
@ -1594,15 +1594,13 @@ mod debug_types {
}
arg.append(f.text(" (+ "))
.append(f.intersperse(
abilities.sorted_iter().map(|ab| f.text(format!("{ab:?}"))),
abilities.sorted_iter().map(|ab| text!(f, "{ab:?}")),
f.text(", "),
))
.append(f.text(")"))
});
f.intersperse(
Some(f.text(format!("{symbol:?}")))
.into_iter()
.chain(fmt_args),
Some(text!(f, "{symbol:?}")).into_iter().chain(fmt_args),
f.text(" "),
)
}

View file

@ -13,7 +13,7 @@ use std::path::PathBuf;
use crate::error::r#type::suggest;
use crate::report::{to_file_problem_report, Annotation, Report, RocDocAllocator, RocDocBuilder};
use ven_pretty::DocAllocator;
use ven_pretty::{text, DocAllocator};
const SYNTAX_PROBLEM: &str = "SYNTAX PROBLEM";
const NAMING_PROBLEM: &str = "NAMING PROBLEM";
@ -352,7 +352,7 @@ pub fn can_problem<'b>(
alloc.reflow("The definition of "),
alloc.symbol_unqualified(alias),
alloc.reflow(" has "),
alloc.text(format!("{}", num_unbound)),
text!(alloc, "{}", num_unbound),
alloc.reflow(" unbound type variables."),
]));
stack.push(alloc.reflow("Here is one occurrence:"));
@ -1748,9 +1748,9 @@ fn pretty_runtime_error<'b>(
alloc.concat([
alloc
.reflow("Roc uses signed 64-bit floating points, allowing values between "),
alloc.text(format!("{:e}", f64::MIN)),
text!(alloc, "{:e}", f64::MIN),
alloc.reflow(" and "),
alloc.text(format!("{:e}", f64::MAX)),
text!(alloc, "{:e}", f64::MAX),
]),
tip,
]);

View file

@ -25,7 +25,7 @@ use roc_types::types::{
RecordField, TypeExt,
};
use std::path::PathBuf;
use ven_pretty::DocAllocator;
use ven_pretty::{text, DocAllocator};
const ADD_ANNOTATIONS: &str = r#"Can more type annotations be added? Type annotations always help me give more specific messages, and I think they could help a lot in this case"#;
@ -213,10 +213,10 @@ pub fn type_problem<'b>(
let stack = [
alloc.concat([
alloc.reflow("Failed to load "),
alloc.text(format!("{:?}", file_path)),
text!(alloc, "{:?}", file_path),
alloc.reflow(" as Str:"),
]),
alloc.text(format!("{}", utf8_err)),
text!(alloc, "{}", utf8_err),
];
Some(Report {
title: "INVALID UTF-8".to_string(),
@ -228,7 +228,7 @@ pub fn type_problem<'b>(
IngestedFileUnsupportedType(file_path, typ) => {
let stack = [
alloc.concat([
alloc.text(format!("{:?}", file_path)),
text!(alloc, "{:?}", file_path),
alloc.reflow(" is annotated to be a "),
alloc.inline_type_block(error_type_to_doc(alloc, typ)),
alloc.reflow("."),
@ -1678,7 +1678,7 @@ fn format_category<'b>(
match category {
Lookup(name) => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
alloc.symbol_foreign_qualified(*name),
alloc.text(" value"),
]),
@ -1687,7 +1687,7 @@ fn format_category<'b>(
If => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
alloc.keyword("if"),
alloc.text(" expression"),
]),
@ -1695,7 +1695,7 @@ fn format_category<'b>(
),
When => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
alloc.keyword("when"),
alloc.text(" expression"),
]),
@ -1730,10 +1730,7 @@ fn format_category<'b>(
alloc.text(" of type:"),
),
IngestedFile(file_path) => (
alloc.concat([
this_is,
alloc.text(format!(" an ingested file ({:?})", file_path)),
]),
alloc.concat([this_is, text!(alloc, " an ingested file ({:?})", file_path)]),
alloc.text(" of type:"),
),
Lambda => (
@ -1747,7 +1744,7 @@ fn format_category<'b>(
OpaqueWrap(opaque) => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
alloc.opaque_name(*opaque),
alloc.text(" opaque wrapping"),
]),
@ -1755,7 +1752,7 @@ fn format_category<'b>(
),
OpaqueArg => (
alloc.concat([alloc.text(format!("{}his argument to an opaque type", t))]),
alloc.concat([text!(alloc, "{}his argument to an opaque type", t)]),
alloc.text(" has type:"),
),
@ -1764,7 +1761,7 @@ fn format_category<'b>(
args_count: 0,
} => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
alloc.tag(name.to_owned()),
alloc.text(" tag"),
]),
@ -1776,7 +1773,7 @@ fn format_category<'b>(
args_count: _,
} => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
alloc.tag(name.to_owned()),
alloc.text(" tag application"),
]),
@ -1790,7 +1787,7 @@ fn format_category<'b>(
Accessor(field) => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
match field {
IndexOrField::Index(index) => alloc.tuple_field(*index),
IndexOrField::Field(field) => alloc.record_field(field.to_owned()),
@ -1801,7 +1798,7 @@ fn format_category<'b>(
),
RecordAccess(field) => (
alloc.concat([
alloc.text(format!("{}he value at ", t)),
text!(alloc, "{}he value at ", t),
alloc.record_field(field.to_owned()),
]),
alloc.text(" is a:"),
@ -1813,10 +1810,7 @@ fn format_category<'b>(
),
TupleAccess(index) => (
alloc.concat([
alloc.text(format!("{}he value at ", t)),
alloc.tuple_field(*index),
]),
alloc.concat([text!(alloc, "{}he value at ", t), alloc.tuple_field(*index)]),
alloc.text(" is a:"),
),
@ -1831,7 +1825,7 @@ fn format_category<'b>(
| BinOp::GreaterThanOrEq,
),
) => (
alloc.text(format!("{}his comparison", t)),
text!(alloc, "{}his comparison", t),
alloc.text(" produces:"),
),
CallResult(Some(_), CalledVia::StringInterpolation) => (
@ -1840,7 +1834,7 @@ fn format_category<'b>(
),
CallResult(Some(symbol), _) => (
alloc.concat([
alloc.text(format!("{}his ", t)),
text!(alloc, "{}his ", t),
alloc.symbol_foreign_qualified(*symbol),
alloc.text(" call"),
]),
@ -4812,8 +4806,7 @@ fn report_record_field_typo<'b>(
} else {
let f = suggestions.remove(0);
let fs = suggestions;
let f_doc = alloc
.text(format!("{}{}{}", field_prefix, field, field_suffix))
let f_doc = text!(alloc, "{}{}{}", field_prefix, field, field_suffix)
.annotate(Annotation::Typo);
let r_doc = match opt_sym {
@ -4832,8 +4825,7 @@ fn report_record_field_typo<'b>(
alloc.reflow("Maybe "),
f_doc,
alloc.reflow(" should be "),
alloc
.text(format!("{}{}{}", field_prefix, f.0, field_suffix))
text!(alloc, "{}{}{}", field_prefix, f.0, field_suffix)
.annotate(Annotation::TypoSuggestion),
alloc.reflow(" instead?"),
]),

View file

@ -5,7 +5,7 @@ use roc_problem::Severity;
use roc_region::all::LineColumnRegion;
use std::path::{Path, PathBuf};
use std::{fmt, io};
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
use ven_pretty::{text, BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
pub use crate::error::canonicalize::can_problem;
pub use crate::error::parse::parse_problem;
@ -393,27 +393,28 @@ impl<'a> RocDocAllocator<'a> {
self.text(symbol.as_str(self.interns))
.annotate(Annotation::Symbol)
} else {
self.text(format!(
text!(
self,
"{}.{}",
symbol.module_string(self.interns),
symbol.as_str(self.interns),
))
)
.annotate(Annotation::Symbol)
}
}
pub fn symbol_qualified(&'a self, symbol: Symbol) -> DocBuilder<'a, Self, Annotation> {
self.text(format!(
text!(
self,
"{}.{}",
symbol.module_string(self.interns),
symbol.as_str(self.interns),
))
)
.annotate(Annotation::Symbol)
}
/// TODO: remove in favor of tag_name
pub fn tag(&'a self, uppercase: Uppercase) -> DocBuilder<'a, Self, Annotation> {
self.text(format!("{}", uppercase))
.annotate(Annotation::Tag)
text!(self, "{}", uppercase).annotate(Annotation::Tag)
}
pub fn opaque_name(&'a self, opaque: Symbol) -> DocBuilder<'a, Self, Annotation> {
@ -434,18 +435,15 @@ impl<'a> RocDocAllocator<'a> {
pub fn wrapped_opaque_name(&'a self, opaque: Symbol) -> DocBuilder<'a, Self, Annotation> {
debug_assert_eq!(opaque.module_id(), self.home, "Opaque wrappings can only be defined in the same module they're defined in, but this one is defined elsewhere: {:?}", opaque);
self.text(format!("@{}", opaque.as_str(self.interns)))
.annotate(Annotation::Opaque)
text!(self, "@{}", opaque.as_str(self.interns)).annotate(Annotation::Opaque)
}
pub fn record_field(&'a self, lowercase: Lowercase) -> DocBuilder<'a, Self, Annotation> {
self.text(format!(".{}", lowercase))
.annotate(Annotation::RecordField)
text!(self, ".{}", lowercase).annotate(Annotation::RecordField)
}
pub fn tuple_field(&'a self, index: usize) -> DocBuilder<'a, Self, Annotation> {
self.text(format!(".{}", index))
.annotate(Annotation::TupleElem)
text!(self, ".{}", index).annotate(Annotation::TupleElem)
}
pub fn module(&'a self, module_id: ModuleId) -> DocBuilder<'a, Self, Annotation> {
@ -779,8 +777,7 @@ impl<'a> RocDocAllocator<'a> {
}
pub fn ident(&'a self, ident: Ident) -> DocBuilder<'a, Self, Annotation> {
self.text(format!("{}", ident.as_inline_str()))
.annotate(Annotation::Symbol)
text!(self, "{}", ident.as_inline_str()).annotate(Annotation::Symbol)
}
pub fn int_literal<I>(&'a self, int: I) -> DocBuilder<'a, Self, Annotation>

View file

@ -12,6 +12,15 @@ mod render;
pub use self::render::TermColored;
pub use self::render::{FmtWrite, IoWrite, Render, RenderAnnotated};
/// Macro to help build a text node with format arguments. It is semantically equivalent to `alloc.text(format(...))`, except
/// that it may not allocate a string at all.
#[macro_export]
macro_rules! text {
($alloc:expr, $($args:tt)*) => {
$alloc.as_string(format_args!($($args)*))
};
}
/// The concrete document type. This type is not meant to be used directly. Instead use the static
/// functions on `Doc` or the methods on an `DocAllocator`.
///