Merge remote-tracking branch 'origin/main' into abilities-syntax

This commit is contained in:
Richard Feldman 2023-08-10 20:29:27 -04:00
commit 2da41be29f
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
524 changed files with 47536 additions and 15089 deletions

View file

@ -469,8 +469,7 @@ impl IAbilitiesStore<Resolved> {
debug_assert!(
old_specialization.is_none(),
"Existing resolution: {:?}",
old_specialization
"Existing resolution: {old_specialization:?}"
);
}

View file

@ -847,29 +847,17 @@ fn can_annotation_help(
let alias = scope.lookup_alias(symbol).unwrap();
local_aliases.insert(symbol, alias.clone());
if vars.is_empty() && env.home == symbol.module_id() {
let actual_var = var_store.fresh();
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
Type::HostExposedAlias {
name: symbol,
type_arguments: vars,
lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(alias.typ.clone()),
actual_var,
}
} else {
Type::Alias {
symbol,
type_arguments: vars.into_iter().map(OptAbleType::unbound).collect(),
lambda_set_variables: alias.lambda_set_variables.clone(),
infer_ext_in_output_types: alias
.infer_ext_in_output_variables
.iter()
.map(|v| Type::Variable(*v))
.collect(),
actual: Box::new(alias.typ.clone()),
kind: alias.kind,
}
Type::Alias {
symbol,
type_arguments: vars.into_iter().map(OptAbleType::unbound).collect(),
lambda_set_variables: alias.lambda_set_variables.clone(),
infer_ext_in_output_types: alias
.infer_ext_in_output_variables
.iter()
.map(|v| Type::Variable(*v))
.collect(),
actual: Box::new(alias.typ.clone()),
kind: alias.kind,
}
}

View file

@ -85,13 +85,19 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
// these are used internally and not tied to a symbol
LowLevel::Hash => unimplemented!(),
LowLevel::PtrCast => unimplemented!(),
LowLevel::PtrWrite => unimplemented!(),
LowLevel::PtrStore => unimplemented!(),
LowLevel::PtrLoad => unimplemented!(),
LowLevel::PtrClearTagId => unimplemented!(),
LowLevel::RefCountIncRcPtr => unimplemented!(),
LowLevel::RefCountDecRcPtr=> unimplemented!(),
LowLevel::RefCountIncDataPtr => unimplemented!(),
LowLevel::RefCountDecDataPtr=> unimplemented!(),
LowLevel::RefCountIsUnique => unimplemented!(),
LowLevel::SetJmp => unimplemented!(),
LowLevel::LongJmp => unimplemented!(),
LowLevel::SetLongJmpBuffer => unimplemented!(),
// these are not implemented, not sure why
LowLevel::StrFromInt => unimplemented!(),
LowLevel::StrFromFloat => unimplemented!(),
@ -118,8 +124,8 @@ map_symbol_to_lowlevel_and_arity! {
StrToUtf8; STR_TO_UTF8; 1,
StrRepeat; STR_REPEAT; 2,
StrTrim; STR_TRIM; 1,
StrTrimLeft; STR_TRIM_LEFT; 1,
StrTrimRight; STR_TRIM_RIGHT; 1,
StrTrimStart; STR_TRIM_START; 1,
StrTrimEnd; STR_TRIM_END; 1,
StrToScalars; STR_TO_SCALARS; 1,
StrGetUnsafe; STR_GET_UNSAFE; 2,
StrSubstringUnsafe; STR_SUBSTRING_UNSAFE; 3,

View file

@ -176,7 +176,7 @@ impl Constraints {
let mut buf = String::new();
writeln!(buf, "Constraints statistics for module {:?}:", module_id)?;
writeln!(buf, "Constraints statistics for module {module_id:?}:")?;
writeln!(buf, " constraints length: {}:", self.constraints.len())?;
writeln!(
@ -833,16 +833,16 @@ impl std::fmt::Debug for Constraint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Eq(Eq(arg0, arg1, arg2, arg3)) => {
write!(f, "Eq({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
write!(f, "Eq({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
}
Self::Store(arg0, arg1, arg2, arg3) => {
write!(f, "Store({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
write!(f, "Store({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
}
Self::Lookup(arg0, arg1, arg2) => {
write!(f, "Lookup({:?}, {:?}, {:?})", arg0, arg1, arg2)
write!(f, "Lookup({arg0:?}, {arg1:?}, {arg2:?})")
}
Self::Pattern(arg0, arg1, arg2, arg3) => {
write!(f, "Pattern({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
}
Self::True => write!(f, "True"),
Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"),
@ -851,27 +851,19 @@ impl std::fmt::Debug for Constraint {
Self::IsOpenType(arg0) => f.debug_tuple("IsOpenType").field(arg0).finish(),
Self::IncludesTag(arg0) => f.debug_tuple("IncludesTag").field(arg0).finish(),
Self::PatternPresence(arg0, arg1, arg2, arg3) => {
write!(
f,
"PatternPresence({:?}, {:?}, {:?}, {:?})",
arg0, arg1, arg2, arg3
)
write!(f, "PatternPresence({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
}
Self::Exhaustive(arg0, arg1, arg2, arg3) => {
write!(
f,
"Exhaustive({:?}, {:?}, {:?}, {:?})",
arg0, arg1, arg2, arg3
)
write!(f, "Exhaustive({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
}
Self::Resolve(arg0) => {
write!(f, "Resolve({:?})", arg0)
write!(f, "Resolve({arg0:?})")
}
Self::CheckCycle(arg0, arg1) => {
write!(f, "CheckCycle({:?}, {:?})", arg0, arg1)
write!(f, "CheckCycle({arg0:?}, {arg1:?})")
}
Self::IngestedFile(arg0, arg1, arg2) => {
write!(f, "IngestedFile({:?}, {:?}, {:?})", arg0, arg1, arg2)
write!(f, "IngestedFile({arg0:?}, {arg1:?}, {arg2:?})")
}
}
}

View file

@ -24,7 +24,7 @@ trait CopyEnv {
if descriptor.copy.into_variable().is_some() {
descriptor.copy = OptVariable::NONE;
} else {
debug_assert!(false, "{:?} marked as copied but it wasn't", var);
debug_assert!(false, "{var:?} marked as copied but it wasn't");
}
})
}
@ -1148,6 +1148,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
})
})
}
ErasedLambda => ErasedLambda,
RangedNumber(range) => {
perform_clone!(RangedNumber(range))
@ -1320,7 +1321,7 @@ mod test {
FlexVar(Some(name)) => {
assert_eq!(subs[*name].as_str(), "a");
}
it => panic!("{:?}", it),
it => panic!("{it:?}"),
}
assert_eq!(var, variant_var);
assert!(matches!(
@ -1337,7 +1338,7 @@ mod test {
FlexVar(Some(name)) => {
assert_eq!(subs[*name].as_str(), "b");
}
it => panic!("{:?}", it),
it => panic!("{it:?}"),
}
match arg.value {
@ -1355,10 +1356,10 @@ mod test {
assert_eq!(name.0.as_str(), "G");
assert_eq!(arguments.len(), 0);
}
e => panic!("{:?}", e),
e => panic!("{e:?}"),
}
}
e => panic!("{:?}", e),
e => panic!("{e:?}"),
}
}
@ -1403,7 +1404,7 @@ mod test {
FlexVar(Some(name)) => {
assert_eq!(target[*name].as_str(), "a");
}
it => panic!("{:?}", it),
it => panic!("{it:?}"),
}
assert_eq!(var, variant_var);
assert!(matches!(
@ -1418,7 +1419,7 @@ mod test {
FlexVar(Some(name)) => {
assert_eq!(target[*name].as_str(), "b");
}
it => panic!("{:?}", it),
it => panic!("{it:?}"),
}
match arg.value {
@ -1436,10 +1437,10 @@ mod test {
assert_eq!(name.0.as_str(), "G");
assert_eq!(arguments.len(), 0);
}
e => panic!("{:?}", e),
e => panic!("{e:?}"),
}
}
e => panic!("{:?}", e),
e => panic!("{e:?}"),
}
}

View file

@ -88,6 +88,23 @@ pub struct Annotation {
pub region: Region,
}
impl Annotation {
fn freshen(mut self, var_store: &mut VarStore) -> Self {
let mut substitutions = MutMap::default();
for v in self.introduced_variables.lambda_sets.iter_mut() {
let new = var_store.fresh();
substitutions.insert(*v, new);
*v = new;
}
self.signature.substitute_variables(&substitutions);
self
}
}
#[derive(Debug)]
pub(crate) struct CanDefs {
defs: Vec<Option<Def>>,
@ -1069,8 +1086,7 @@ fn canonicalize_value_defs<'a>(
debug_assert_eq!(env.home, s.module_id());
debug_assert!(
!symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()),
"{:?}",
s
"{s:?}"
);
symbol_to_index.push((s.ident_id(), def_index as u32));
@ -1639,6 +1655,14 @@ pub(crate) fn sort_can_defs_new(
}
};
let host_annotation = if exposed_symbols.contains(&symbol) {
def.annotation
.clone()
.map(|a| (var_store.fresh(), a.freshen(var_store)))
} else {
None
};
if is_initial && !exposed_symbols.contains(&symbol) {
env.problem(Problem::DefsOnlyUsedInRecursion(1, def.region()));
}
@ -1650,6 +1674,7 @@ pub(crate) fn sort_can_defs_new(
Loc::at(def.loc_expr.region, closure_data),
def.expr_var,
def.annotation,
host_annotation,
specializes,
);
}
@ -1659,55 +1684,80 @@ pub(crate) fn sort_can_defs_new(
def.loc_expr,
def.expr_var,
def.annotation,
host_annotation,
specializes,
);
}
}
} else {
match def.loc_pattern.value {
Pattern::Identifier(symbol) => match def.loc_expr.value {
Closure(closure_data) => {
declarations.push_function_def(
Loc::at(def.loc_pattern.region, symbol),
Loc::at(def.loc_expr.region, closure_data),
def.expr_var,
def.annotation,
None,
);
Pattern::Identifier(symbol) => {
let host_annotation = if exposed_symbols.contains(&symbol) {
def.annotation
.clone()
.map(|a| (var_store.fresh(), a.freshen(var_store)))
} else {
None
};
match def.loc_expr.value {
Closure(closure_data) => {
declarations.push_function_def(
Loc::at(def.loc_pattern.region, symbol),
Loc::at(def.loc_expr.region, closure_data),
def.expr_var,
def.annotation,
host_annotation,
None,
);
}
_ => {
declarations.push_value_def(
Loc::at(def.loc_pattern.region, symbol),
def.loc_expr,
def.expr_var,
def.annotation,
host_annotation,
None,
);
}
}
_ => {
declarations.push_value_def(
Loc::at(def.loc_pattern.region, symbol),
def.loc_expr,
def.expr_var,
def.annotation,
None,
);
}
},
}
Pattern::AbilityMemberSpecialization {
ident: symbol,
specializes,
} => match def.loc_expr.value {
Closure(closure_data) => {
declarations.push_function_def(
Loc::at(def.loc_pattern.region, symbol),
Loc::at(def.loc_expr.region, closure_data),
def.expr_var,
def.annotation,
Some(specializes),
);
} => {
let host_annotation = if exposed_symbols.contains(&symbol) {
def.annotation
.clone()
.map(|a| (var_store.fresh(), a.freshen(var_store)))
} else {
None
};
match def.loc_expr.value {
Closure(closure_data) => {
declarations.push_function_def(
Loc::at(def.loc_pattern.region, symbol),
Loc::at(def.loc_expr.region, closure_data),
def.expr_var,
def.annotation,
host_annotation,
Some(specializes),
);
}
_ => {
declarations.push_value_def(
Loc::at(def.loc_pattern.region, symbol),
def.loc_expr,
def.expr_var,
def.annotation,
host_annotation,
Some(specializes),
);
}
}
_ => {
declarations.push_value_def(
Loc::at(def.loc_pattern.region, symbol),
def.loc_expr,
def.expr_var,
def.annotation,
Some(specializes),
);
}
},
}
_ => {
declarations.push_destructure_def(
def.loc_pattern,
@ -1750,6 +1800,14 @@ pub(crate) fn sort_can_defs_new(
Some(r) => Some(Region::span_across(&r, &def.region())),
};
let host_annotation = if exposed_symbols.contains(&symbol) {
def.annotation
.clone()
.map(|a| (var_store.fresh(), a.freshen(var_store)))
} else {
None
};
match def.loc_expr.value {
Closure(closure_data) => {
declarations.push_recursive_def(
@ -1757,6 +1815,7 @@ pub(crate) fn sort_can_defs_new(
Loc::at(def.loc_expr.region, closure_data),
def.expr_var,
def.annotation,
host_annotation,
specializes,
);
}
@ -1766,6 +1825,7 @@ pub(crate) fn sort_can_defs_new(
def.loc_expr,
def.expr_var,
def.annotation,
host_annotation,
specializes,
);
}
@ -1838,7 +1898,7 @@ pub(crate) fn sort_can_defs(
);
let declaration = if def_ordering.references.get_row_col(index, index) {
debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {:?} now, that's a bug!", def);
debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {def:?} now, that's a bug!");
if is_initial
&& !def
@ -2272,12 +2332,18 @@ fn canonicalize_pending_body<'a>(
opt_loc_annotation: Option<Loc<crate::annotation::Annotation>>,
) -> DefOutput {
let mut loc_value = &loc_expr.value;
while let ast::Expr::ParensAround(value) = loc_value {
loc_value = value;
}
// We treat closure definitions `foo = \a, b -> ...` differently from other body expressions,
// because they need more bookkeeping (for tail calls, closure captures, etc.)
//
// Only defs of the form `foo = ...` can be closure declarations or self tail calls.
let (loc_can_expr, def_references) = {
match (&loc_can_pattern.value, &loc_expr.value) {
match (&loc_can_pattern.value, &loc_value) {
(
Pattern::Identifier(defined_symbol)
| Pattern::AbilityMemberSpecialization {

View file

@ -222,16 +222,16 @@ pub(crate) fn synthesize_member_impl<'a>(
ability_member: Symbol,
) -> (Symbol, Loc<Pattern>, &'a Loc<ast::Expr<'a>>) {
// @Opaq
let at_opaque = env.arena.alloc_str(&format!("@{}", opaque_name));
let at_opaque = env.arena.alloc_str(&format!("@{opaque_name}"));
let (impl_name, def_body): (String, ast::Expr<'a>) = match ability_member {
Symbol::ENCODE_TO_ENCODER => (
format!("#{}_toEncoder", opaque_name),
format!("#{opaque_name}_toEncoder"),
to_encoder(env, at_opaque),
),
Symbol::DECODE_DECODER => (format!("#{}_decoder", opaque_name), decoder(env, at_opaque)),
Symbol::HASH_HASH => (format!("#{}_hash", opaque_name), hash(env, at_opaque)),
Symbol::BOOL_IS_EQ => (format!("#{}_isEq", opaque_name), is_eq(env, at_opaque)),
Symbol::DECODE_DECODER => (format!("#{opaque_name}_decoder"), decoder(env, at_opaque)),
Symbol::HASH_HASH => (format!("#{opaque_name}_hash"), hash(env, at_opaque)),
Symbol::BOOL_IS_EQ => (format!("#{opaque_name}_isEq"), is_eq(env, at_opaque)),
other => internal_error!("{:?} is not a derivable ability member!", other),
};

View file

@ -1362,7 +1362,7 @@ pub fn build_host_exposed_def(
match typ.shallow_structural_dealias() {
Type::Function(args, _, _) => {
for i in 0..args.len() {
let name = format!("closure_arg_{}_{}", ident, i);
let name = format!("closure_arg_{ident}_{i}");
let arg_symbol = {
let ident = name.clone().into();
@ -1381,7 +1381,7 @@ pub fn build_host_exposed_def(
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var)));
}
let foreign_symbol_name = format!("roc_fx_{}", ident);
let foreign_symbol_name = format!("roc_fx_{ident}");
let low_level_call = Expr::ForeignCall {
foreign_symbol: foreign_symbol_name.into(),
args: linked_symbol_arguments,
@ -1389,7 +1389,7 @@ pub fn build_host_exposed_def(
};
let effect_closure_symbol = {
let name = format!("effect_closure_{}", ident);
let name = format!("effect_closure_{ident}");
let ident = name.into();
scope.introduce(ident, Region::zero()).unwrap()
@ -1435,7 +1435,7 @@ pub fn build_host_exposed_def(
_ => {
// not a function
let foreign_symbol_name = format!("roc_fx_{}", ident);
let foreign_symbol_name = format!("roc_fx_{ident}");
let low_level_call = Expr::ForeignCall {
foreign_symbol: foreign_symbol_name.into(),
args: linked_symbol_arguments,
@ -1443,7 +1443,7 @@ pub fn build_host_exposed_def(
};
let effect_closure_symbol = {
let name = format!("effect_closure_{}", ident);
let name = format!("effect_closure_{ident}");
let ident = name.into();
scope.introduce(ident, Region::zero()).unwrap()

View file

@ -67,8 +67,7 @@ impl<'a> Env<'a> {
) -> Result<Symbol, RuntimeError> {
debug_assert!(
!module_name_str.is_empty(),
"Called env.qualified_lookup with an unqualified ident: {:?}",
ident
"Called env.qualified_lookup with an unqualified ident: {ident:?}"
);
let module_name = ModuleName::from(module_name_str);

View file

@ -140,6 +140,7 @@ fn index_var(
| Content::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _)
| Content::LambdaSet(_)
| Content::ErasedLambda
| Content::RangedNumber(..) => return Err(TypeError),
Content::Error => return Err(TypeError),
Content::RecursionVar {

View file

@ -18,7 +18,7 @@ use roc_module::called_via::CalledVia;
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_parse::ast::{self, Defs, StrLiteral};
use roc_parse::ast::{self, Defs, PrecedenceConflict, StrLiteral};
use roc_parse::ident::Accessor;
use roc_parse::pattern::PatternType::*;
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
@ -1403,14 +1403,13 @@ pub fn canonicalize_expr<'a>(
(answer, Output::default())
}
&ast::Expr::ParensAround(sub_expr) => {
let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, sub_expr);
(loc_expr.value, output)
}
// Below this point, we shouln't see any of these nodes anymore because
// operator desugaring should have removed them!
bad_expr @ ast::Expr::ParensAround(_) => {
internal_error!(
"A ParensAround did not get removed during operator desugaring somehow: {:#?}",
bad_expr
);
}
bad_expr @ ast::Expr::SpaceBefore(_, _) => {
internal_error!(
"A SpaceBefore did not get removed during operator desugaring somehow: {:#?}",
@ -2377,11 +2376,115 @@ fn flatten_str_literal<'a>(
}
}
/// Comments, newlines, and nested interpolation are disallowed inside interpolation
pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
match expr {
ast::Expr::Var { .. } => true,
ast::Expr::RecordAccess(sub_expr, _) => is_valid_interpolation(sub_expr),
_ => false,
// These definitely contain neither comments nor newlines, so they are valid
ast::Expr::Var { .. }
| ast::Expr::SingleQuote(_)
| ast::Expr::Str(StrLiteral::PlainLine(_))
| ast::Expr::Float(_)
| ast::Expr::Num(_)
| ast::Expr::NonBase10Int { .. }
| ast::Expr::AccessorFunction(_)
| ast::Expr::Crash
| ast::Expr::Underscore(_)
| ast::Expr::MalformedIdent(_, _)
| ast::Expr::Tag(_)
| ast::Expr::OpaqueRef(_)
| ast::Expr::MalformedClosure => true,
// Newlines are disallowed inside interpolation, and these all require newlines
ast::Expr::Dbg(_, _)
| ast::Expr::Defs(_, _)
| ast::Expr::Expect(_, _)
| ast::Expr::When(_, _)
| ast::Expr::Backpassing(_, _, _)
| ast::Expr::IngestedFile(_, _)
| ast::Expr::SpaceBefore(_, _)
| ast::Expr::Str(StrLiteral::Block(_))
| ast::Expr::SpaceAfter(_, _) => false,
// These can contain subexpressions, so we need to recursively check those
ast::Expr::Str(StrLiteral::Line(segments)) => {
segments.iter().all(|segment| match segment {
ast::StrSegment::EscapedChar(_)
| ast::StrSegment::Unicode(_)
| ast::StrSegment::Plaintext(_) => true,
// Disallow nested interpolation. Alternatively, we could allow it but require
// a comment above it apologizing to the next person who has to read the code.
ast::StrSegment::Interpolated(_) => false,
})
}
ast::Expr::Record(fields) => fields.iter().all(|loc_field| match loc_field.value {
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
}
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
ast::AssignedField::SpaceBefore(_, _) | ast::AssignedField::SpaceAfter(_, _) => false,
}),
ast::Expr::Tuple(fields) => fields
.iter()
.all(|loc_field| is_valid_interpolation(&loc_field.value)),
ast::Expr::MultipleRecordBuilders(loc_expr)
| ast::Expr::UnappliedRecordBuilder(loc_expr)
| ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. })
| ast::Expr::UnaryOp(loc_expr, _)
| ast::Expr::Closure(_, loc_expr) => is_valid_interpolation(&loc_expr.value),
ast::Expr::TupleAccess(sub_expr, _)
| ast::Expr::ParensAround(sub_expr)
| ast::Expr::RecordAccess(sub_expr, _) => is_valid_interpolation(sub_expr),
ast::Expr::Apply(loc_expr, args, _called_via) => {
is_valid_interpolation(&loc_expr.value)
&& args
.iter()
.all(|loc_arg| is_valid_interpolation(&loc_arg.value))
}
ast::Expr::BinOps(loc_exprs, loc_expr) => {
is_valid_interpolation(&loc_expr.value)
&& loc_exprs
.iter()
.all(|(loc_expr, _binop)| is_valid_interpolation(&loc_expr.value))
}
ast::Expr::If(branches, final_branch) => {
is_valid_interpolation(&final_branch.value)
&& branches.iter().all(|(loc_before, loc_after)| {
is_valid_interpolation(&loc_before.value)
&& is_valid_interpolation(&loc_after.value)
})
}
ast::Expr::List(elems) => elems
.iter()
.all(|loc_expr| is_valid_interpolation(&loc_expr.value)),
ast::Expr::RecordUpdate { update, fields } => {
is_valid_interpolation(&update.value)
&& fields.iter().all(|loc_field| match loc_field.value {
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
}
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
ast::AssignedField::SpaceBefore(_, _)
| ast::AssignedField::SpaceAfter(_, _) => false,
})
}
ast::Expr::RecordBuilder(fields) => fields.iter().all(|loc_field| match loc_field.value {
ast::RecordBuilderField::Value(_label, comments, loc_expr) => {
comments.is_empty() && is_valid_interpolation(&loc_expr.value)
}
ast::RecordBuilderField::ApplyValue(
_label,
comments_before,
comments_after,
loc_expr,
) => {
comments_before.is_empty()
&& comments_after.is_empty()
&& is_valid_interpolation(&loc_expr.value)
}
ast::RecordBuilderField::Malformed(_) | ast::RecordBuilderField::LabelOnly(_) => true,
ast::RecordBuilderField::SpaceBefore(_, _)
| ast::RecordBuilderField::SpaceAfter(_, _) => false,
}),
}
}
@ -2536,6 +2639,8 @@ pub struct Declarations {
// used for ability member specializatons.
pub specializes: VecMap<usize, Symbol>,
pub host_exposed_annotations: VecMap<usize, (Variable, crate::def::Annotation)>,
pub function_bodies: Vec<Loc<FunctionDef>>,
pub expressions: Vec<Loc<Expr>>,
pub destructs: Vec<DestructureDef>,
@ -2557,6 +2662,7 @@ impl Declarations {
variables: Vec::with_capacity(capacity),
symbols: Vec::with_capacity(capacity),
annotations: Vec::with_capacity(capacity),
host_exposed_annotations: VecMap::new(),
function_bodies: Vec::with_capacity(capacity),
expressions: Vec::with_capacity(capacity),
specializes: VecMap::default(), // number of specializations is probably low
@ -2587,6 +2693,7 @@ impl Declarations {
loc_closure_data: Loc<ClosureData>,
expr_var: Variable,
annotation: Option<Annotation>,
host_annotation: Option<(Variable, Annotation)>,
specializes: Option<Symbol>,
) -> usize {
let index = self.declarations.len();
@ -2609,6 +2716,11 @@ impl Declarations {
Recursive::TailRecursive => DeclarationTag::TailRecursive(function_def_index),
};
if let Some(annotation) = host_annotation {
self.host_exposed_annotations
.insert(self.declarations.len(), annotation);
}
self.declarations.push(tag);
self.variables.push(expr_var);
self.symbols.push(symbol);
@ -2629,6 +2741,7 @@ impl Declarations {
loc_closure_data: Loc<ClosureData>,
expr_var: Variable,
annotation: Option<Annotation>,
host_annotation: Option<(Variable, Annotation)>,
specializes: Option<Symbol>,
) -> usize {
let index = self.declarations.len();
@ -2644,6 +2757,11 @@ impl Declarations {
let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def);
if let Some(annotation) = host_annotation {
self.host_exposed_annotations
.insert(self.declarations.len(), annotation);
}
self.declarations
.push(DeclarationTag::Function(function_def_index));
self.variables.push(expr_var);
@ -2701,10 +2819,16 @@ impl Declarations {
loc_expr: Loc<Expr>,
expr_var: Variable,
annotation: Option<Annotation>,
host_annotation: Option<(Variable, Annotation)>,
specializes: Option<Symbol>,
) -> usize {
let index = self.declarations.len();
if let Some(annotation) = host_annotation {
self.host_exposed_annotations
.insert(self.declarations.len(), annotation);
}
self.declarations.push(DeclarationTag::Value);
self.variables.push(expr_var);
self.symbols.push(symbol);
@ -2759,6 +2883,7 @@ impl Declarations {
def.expr_var,
def.annotation,
None,
None,
);
}
@ -2769,6 +2894,7 @@ impl Declarations {
def.expr_var,
def.annotation,
None,
None,
);
}
},
@ -2779,6 +2905,7 @@ impl Declarations {
def.expr_var,
def.annotation,
None,
None,
);
}
},

View file

@ -347,9 +347,7 @@ pub fn canonicalize_module_defs<'a>(
// the symbol should already be added to the scope when this module is canonicalized
debug_assert!(
scope.contains_alias(symbol) || scope.abilities_store.is_ability(symbol),
"The {:?} is not a type alias or ability known in {:?}",
symbol,
home
"The {symbol:?} is not a type alias or ability known in {home:?}"
);
// but now we know this symbol by a different identifier, so we still need to add it to

View file

@ -220,8 +220,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result<ParsedNumResult, IntErrorKind
assert!(
(2..=36).contains(&radix),
"from_str_radix_int: must lie in the range `[2, 36]` - found {}",
radix
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}"
);
let (opt_exact_bound, src) = parse_literal_suffix(src);

View file

@ -7,7 +7,9 @@ use roc_module::called_via::BinOp::Pizza;
use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *};
use roc_parse::ast::{AssignedField, Collection, RecordBuilderField, ValueDef, WhenBranch};
use roc_parse::ast::{
AssignedField, Collection, RecordBuilderField, StrLiteral, StrSegment, ValueDef, WhenBranch,
};
use roc_region::all::{Loc, Region};
// BinOp precedence logic adapted from Gluon by Markus Westerlind
@ -129,7 +131,6 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
Float(..)
| Num(..)
| NonBase10Int { .. }
| Str(_)
| SingleQuote(_)
| AccessorFunction(_)
| Var { .. }
@ -144,6 +145,28 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
| IngestedFile(_, _)
| Crash => loc_expr,
Str(str_literal) => match str_literal {
StrLiteral::PlainLine(_) => loc_expr,
StrLiteral::Line(segments) => {
let region = loc_expr.region;
let value = Str(StrLiteral::Line(desugar_str_segments(arena, segments)));
arena.alloc(Loc { region, value })
}
StrLiteral::Block(lines) => {
let region = loc_expr.region;
let new_lines = Vec::from_iter_in(
lines
.iter()
.map(|segments| desugar_str_segments(arena, segments)),
arena,
);
let value = Str(StrLiteral::Block(new_lines.into_bump_slice()));
arena.alloc(Loc { region, value })
}
},
TupleAccess(sub_expr, paths) => {
let region = loc_expr.region;
let loc_sub_expr = Loc {
@ -288,7 +311,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
break builder_arg.closure;
}
SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => {
SpaceBefore(expr, _) | SpaceAfter(expr, _) => {
current = *expr;
}
_ => break loc_arg,
@ -382,7 +405,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
region: loc_expr.region,
})
}
SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => {
SpaceBefore(expr, _) | SpaceAfter(expr, _) => {
// Since we've already begun canonicalization, spaces and parens
// are no longer needed and should be dropped.
desugar_expr(
@ -393,6 +416,20 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
}),
)
}
ParensAround(expr) => {
let desugared = desugar_expr(
arena,
arena.alloc(Loc {
value: **expr,
region: loc_expr.region,
}),
);
arena.alloc(Loc {
value: ParensAround(&desugared.value),
region: loc_expr.region,
})
}
If(if_thens, final_else_branch) => {
// If does not get desugared into `when` so we can give more targeted error messages during type checking.
let desugared_final_else = &*arena.alloc(desugar_expr(arena, final_else_branch));
@ -430,6 +467,34 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
}
}
fn desugar_str_segments<'a>(
arena: &'a Bump,
segments: &'a [StrSegment<'a>],
) -> &'a [StrSegment<'a>] {
Vec::from_iter_in(
segments.iter().map(|segment| match segment {
StrSegment::Plaintext(_) | StrSegment::Unicode(_) | StrSegment::EscapedChar(_) => {
*segment
}
StrSegment::Interpolated(loc_expr) => {
let loc_desugared = desugar_expr(
arena,
arena.alloc(Loc {
region: loc_expr.region,
value: *loc_expr.value,
}),
);
StrSegment::Interpolated(Loc {
region: loc_desugared.region,
value: arena.alloc(loc_desugared.value),
})
}
}),
arena,
)
.into_bump_slice()
}
fn desugar_field<'a>(
arena: &'a Bump,
field: &'a AssignedField<'a, Expr<'a>>,

View file

@ -531,7 +531,7 @@ pub fn canonicalize_pattern<'a>(
use std::ops::Neg;
let sign_str = if is_negative { "-" } else { "" };
let int_str = format!("{}{}", sign_str, int).into_boxed_str();
let int_str = format!("{sign_str}{int}").into_boxed_str();
let i = match int {
// Safety: this is fine because I128::MAX = |I128::MIN| - 1
IntValue::I128(n) if is_negative => {