mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into wasm-linking-zig9
This commit is contained in:
commit
c950f6d834
106 changed files with 4163 additions and 1740 deletions
2
AUTHORS
2
AUTHORS
|
@ -72,3 +72,5 @@ Elliot Waite <1767836+elliotwaite@users.noreply.github.com>
|
|||
zimt28 <1764689+zimt28@users.noreply.github.com>
|
||||
Ananda Umamil <zweimach@zweimach.org>
|
||||
SylvanSign <jake.d.bray@gmail.com>
|
||||
Nikita Mounier <36044205+nikitamounier@users.noreply.github.com>
|
||||
Cai Bingjun <62678643+C-BJ@users.noreply.github.com>
|
||||
|
|
|
@ -39,6 +39,8 @@ Use `cargo run help` to see all subcommands.
|
|||
To use the `repl` subcommand, execute `cargo run repl`.
|
||||
Use `cargo build` to build the whole project.
|
||||
|
||||
> When using `nix-shell`, make sure that if you start `nix-shell` and then run `echo "$PATH" | tr ':' '\n'`, you see the `usr/bin` path listed after all the `/nix/…` paths. Otherwise you might get some nasty rust compilation errors!
|
||||
|
||||
#### Extra tips
|
||||
|
||||
If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.net/) and [lorri](https://github.com/nix-community/lorri). Whenever you `cd` into `roc/`, they will automatically load the Nix dependencies into your current shell, so you never have to run nix-shell directly!
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3370,6 +3370,7 @@ dependencies = [
|
|||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_constrain",
|
||||
"roc_error_macros",
|
||||
"roc_gen_dev",
|
||||
"roc_gen_llvm",
|
||||
"roc_gen_wasm",
|
||||
|
|
5
FAQ.md
5
FAQ.md
|
@ -1,5 +1,10 @@
|
|||
# Frequently Asked Questions
|
||||
|
||||
# Why make a new editor instead of making an LSP plugin for VSCode, Vim or Emacs?
|
||||
The Roc editor is one of the key areas where we want to innovate. Constraining ourselves to a plugin for existing editors would severely limit our possibilities for innovation.
|
||||
|
||||
A key part of our editor will be the use of plugins that are shipped with libraries. Think of a regex visualizer, parser debugger, or color picker. For library authors, it would be most convenient to write these plugins in Roc. Trying to dynamically load library plugins (written in Roc) in for example VSCode seems very difficult.
|
||||
|
||||
## Is there syntax highlighting for Vim/Emacs/VS Code or a LSP?
|
||||
|
||||
Not currently. Although they will presumably exist someday, while Roc is in the early days there's actually a conscious
|
||||
|
|
|
@ -16,9 +16,9 @@ use roc_collections::all::{default_hasher, ImMap, MutMap, MutSet, SendMap};
|
|||
use roc_error_macros::{todo_abilities, todo_opaques};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{self, TypeHeader};
|
||||
use roc_parse::ast::{self, TypeDef, TypeHeader, ValueDef as AstValueDef};
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::{Problem, RuntimeError};
|
||||
use roc_problem::can::{Problem, RuntimeError, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use std::collections::HashMap;
|
||||
|
@ -133,7 +133,7 @@ fn to_pending_def<'a>(
|
|||
use roc_parse::ast::Def::*;
|
||||
|
||||
match def {
|
||||
Annotation(loc_pattern, loc_ann) => {
|
||||
Value(AstValueDef::Annotation(loc_pattern, loc_ann)) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = pattern::to_pattern_id(
|
||||
env,
|
||||
|
@ -148,7 +148,7 @@ fn to_pending_def<'a>(
|
|||
PendingDef::AnnotationOnly(loc_pattern, loc_can_pattern, loc_ann),
|
||||
))
|
||||
}
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
Value(AstValueDef::Body(loc_pattern, loc_expr)) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = pattern::to_pattern_id(
|
||||
env,
|
||||
|
@ -164,13 +164,13 @@ fn to_pending_def<'a>(
|
|||
))
|
||||
}
|
||||
|
||||
AnnotatedBody {
|
||||
Value(AstValueDef::AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: _,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => {
|
||||
}) => {
|
||||
if ann_pattern.value.equivalent(&body_pattern.value) {
|
||||
// NOTE: Pick the body pattern, picking the annotation one is
|
||||
// incorrect in the presence of optional record fields!
|
||||
|
@ -199,10 +199,10 @@ fn to_pending_def<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
roc_parse::ast::Def::Alias {
|
||||
Type(TypeDef::Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
} => {
|
||||
}) => {
|
||||
let region = Region::span_across(&name.region, &ann.region);
|
||||
|
||||
match scope.introduce(
|
||||
|
@ -251,9 +251,10 @@ fn to_pending_def<'a>(
|
|||
}
|
||||
|
||||
Err((original_region, loc_shadowed_symbol)) => {
|
||||
env.problem(Problem::ShadowingInAnnotation {
|
||||
env.problem(Problem::Shadowing {
|
||||
original_region,
|
||||
shadow: loc_shadowed_symbol,
|
||||
kind: ShadowKind::Variable,
|
||||
});
|
||||
|
||||
Some((Output::default(), PendingDef::InvalidAlias))
|
||||
|
@ -261,10 +262,10 @@ fn to_pending_def<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Opaque { .. } => todo_opaques!(),
|
||||
Ability { .. } => todo_abilities!(),
|
||||
Type(TypeDef::Opaque { .. }) => todo_opaques!(),
|
||||
Type(TypeDef::Ability { .. }) => todo_abilities!(),
|
||||
|
||||
Expect(_) => todo!(),
|
||||
Value(AstValueDef::Expect(_)) => todo!(),
|
||||
|
||||
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {
|
||||
to_pending_def(env, sub_def, scope, pattern_type)
|
||||
|
@ -608,7 +609,7 @@ fn canonicalize_pending_def<'a>(
|
|||
pattern_id: loc_can_pattern,
|
||||
expr_id: env.pool.add(loc_can_expr),
|
||||
type_id: annotation,
|
||||
rigids: rigids,
|
||||
rigids,
|
||||
expr_var: env.var_store.fresh(),
|
||||
};
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ pub fn def_to_def2<'a>(
|
|||
def_to_def2(arena, env, scope, inner_def, region)
|
||||
}
|
||||
}
|
||||
Body(&loc_pattern, &loc_expr) => {
|
||||
Value(roc_parse::ast::ValueDef::Body(&loc_pattern, &loc_expr)) => {
|
||||
let expr2 = loc_expr_to_expr2(arena, loc_expr, env, scope, region).0;
|
||||
let expr_id = env.pool.add(expr2);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use roc_error_macros::todo_opaques;
|
|||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError};
|
||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
|
||||
use roc_region::all::Region;
|
||||
use roc_types::subs::Variable;
|
||||
|
||||
|
@ -161,6 +161,7 @@ pub fn to_pattern2<'a>(
|
|||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}));
|
||||
|
||||
let name: &str = shadow.value.as_ref();
|
||||
|
@ -364,6 +365,7 @@ pub fn to_pattern2<'a>(
|
|||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}));
|
||||
|
||||
// let shadowed = Pattern2::Shadowed {
|
||||
|
@ -443,6 +445,7 @@ pub fn to_pattern2<'a>(
|
|||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}));
|
||||
|
||||
// No matter what the other patterns
|
||||
|
|
|
@ -246,6 +246,11 @@ pub fn build_file<'a>(
|
|||
todo!("gracefully handle failing to surgically link");
|
||||
})?;
|
||||
BuildOutcome::NoProblems
|
||||
} else if matches!(link_type, LinkType::None) {
|
||||
// Just copy the object file to the output folder.
|
||||
binary_path.set_extension(app_extension);
|
||||
std::fs::copy(app_o_file, &binary_path).unwrap();
|
||||
BuildOutcome::NoProblems
|
||||
} else {
|
||||
let mut inputs = vec![
|
||||
host_input_path.as_path().to_str().unwrap(),
|
||||
|
|
|
@ -10,8 +10,8 @@ use roc_fmt::module::fmt_module;
|
|||
use roc_fmt::Buf;
|
||||
use roc_module::called_via::{BinOp, UnaryOp};
|
||||
use roc_parse::ast::{
|
||||
AbilityDemand, AssignedField, Collection, Expr, Has, HasClause, Pattern, Spaced, StrLiteral,
|
||||
StrSegment, Tag, TypeAnnotation, TypeHeader, WhenBranch,
|
||||
AbilityMember, AssignedField, Collection, Expr, Has, HasClause, Pattern, Spaced, StrLiteral,
|
||||
StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch,
|
||||
};
|
||||
use roc_parse::header::{
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
||||
|
@ -445,62 +445,80 @@ impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Def<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
use TypeDef::*;
|
||||
|
||||
match *self {
|
||||
Def::Annotation(a, b) => {
|
||||
Def::Annotation(a.remove_spaces(arena), b.remove_spaces(arena))
|
||||
}
|
||||
Def::Alias {
|
||||
Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
} => Def::Alias {
|
||||
} => Alias {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
ann: ann.remove_spaces(arena),
|
||||
},
|
||||
Def::Opaque {
|
||||
Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
typ,
|
||||
} => Def::Opaque {
|
||||
} => Opaque {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
typ: typ.remove_spaces(arena),
|
||||
},
|
||||
Def::Body(a, b) => Def::Body(
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_has,
|
||||
members,
|
||||
} => Ability {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
loc_has: loc_has.remove_spaces(arena),
|
||||
members: members.remove_spaces(arena),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
use ValueDef::*;
|
||||
|
||||
match *self {
|
||||
Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)),
|
||||
Body(a, b) => Body(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Def::AnnotatedBody {
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: _,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => Def::AnnotatedBody {
|
||||
} => AnnotatedBody {
|
||||
ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)),
|
||||
ann_type: arena.alloc(ann_type.remove_spaces(arena)),
|
||||
comment: None,
|
||||
body_pattern: arena.alloc(body_pattern.remove_spaces(arena)),
|
||||
body_expr: arena.alloc(body_expr.remove_spaces(arena)),
|
||||
},
|
||||
Def::Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_has,
|
||||
demands,
|
||||
} => Def::Ability {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
loc_has: loc_has.remove_spaces(arena),
|
||||
demands: demands.remove_spaces(arena),
|
||||
},
|
||||
Def::Expect(a) => Def::Expect(arena.alloc(a.remove_spaces(arena))),
|
||||
Expect(a) => Expect(arena.alloc(a.remove_spaces(arena))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Def<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Def::Type(def) => Def::Type(def.remove_spaces(arena)),
|
||||
Def::Value(def) => Def::Value(def.remove_spaces(arena)),
|
||||
Def::NotYetImplemented(a) => Def::NotYetImplemented(a),
|
||||
Def::SpaceBefore(a, _) | Def::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
|
@ -513,9 +531,9 @@ impl<'a> RemoveSpaces<'a> for Has<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for AbilityDemand<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for AbilityMember<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
AbilityDemand {
|
||||
AbilityMember {
|
||||
name: self.name.remove_spaces(arena),
|
||||
typ: self.typ.remove_spaces(arena),
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ pub const FLAG_DEV: &str = "dev";
|
|||
pub const FLAG_OPTIMIZE: &str = "optimize";
|
||||
pub const FLAG_OPT_SIZE: &str = "opt-size";
|
||||
pub const FLAG_LIB: &str = "lib";
|
||||
pub const FLAG_NO_LINK: &str = "no-link";
|
||||
pub const FLAG_TARGET: &str = "target";
|
||||
pub const FLAG_TIME: &str = "time";
|
||||
pub const FLAG_LINK: &str = "roc-linker";
|
||||
|
@ -88,6 +89,12 @@ pub fn build_app<'a>() -> App<'a> {
|
|||
.about("Build a C library instead of an executable.")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(FLAG_NO_LINK)
|
||||
.long(FLAG_NO_LINK)
|
||||
.about("Does not link. Instead just outputs the `.o` file")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(FLAG_DEBUG)
|
||||
.long(FLAG_DEBUG)
|
||||
|
@ -291,10 +298,14 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
|||
let emit_debug_info = matches.is_present(FLAG_DEBUG);
|
||||
let emit_timings = matches.is_present(FLAG_TIME);
|
||||
|
||||
let link_type = if matches.is_present(FLAG_LIB) {
|
||||
LinkType::Dylib
|
||||
} else {
|
||||
LinkType::Executable
|
||||
let link_type = match (
|
||||
matches.is_present(FLAG_LIB),
|
||||
matches.is_present(FLAG_NO_LINK),
|
||||
) {
|
||||
(true, false) => LinkType::Dylib,
|
||||
(true, true) => user_error!("build can only be one of `--lib` or `--no-link`"),
|
||||
(false, true) => LinkType::None,
|
||||
(false, false) => LinkType::Executable,
|
||||
};
|
||||
let surgically_link = matches.is_present(FLAG_LINK);
|
||||
let precompiled = matches.is_present(FLAG_PRECOMPILED);
|
||||
|
|
|
@ -24,6 +24,7 @@ roc_gen_llvm = { path = "../gen_llvm", optional = true }
|
|||
roc_gen_wasm = { path = "../gen_wasm", optional = true }
|
||||
roc_gen_dev = { path = "../gen_dev", default-features = false }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
roc_error_macros = { path = "../../error_macros" }
|
||||
roc_std = { path = "../../roc_std", default-features = false }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
libloading = "0.7.1"
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::target::{arch_str, target_zig_str};
|
|||
#[cfg(feature = "llvm")]
|
||||
use libloading::{Error, Library};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_error_macros::internal_error;
|
||||
// #[cfg(feature = "llvm")]
|
||||
use roc_mono::ir::OptLevel;
|
||||
use std::collections::HashMap;
|
||||
|
@ -20,10 +21,10 @@ fn zig_executable() -> String {
|
|||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum LinkType {
|
||||
// These numbers correspond to the --lib flag; if it's present
|
||||
// (e.g. is_present returns `1 as bool`), this will be 1 as well.
|
||||
// These numbers correspond to the --lib and --no-link flags
|
||||
Executable = 0,
|
||||
Dylib = 1,
|
||||
None = 2,
|
||||
}
|
||||
|
||||
/// input_paths can include the host as well as the app. e.g. &["host.o", "roc_app.o"]
|
||||
|
@ -835,6 +836,7 @@ fn link_linux(
|
|||
output_path,
|
||||
)
|
||||
}
|
||||
LinkType::None => internal_error!("link_linux should not be called with link type of none"),
|
||||
};
|
||||
|
||||
let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string());
|
||||
|
@ -904,6 +906,7 @@ fn link_macos(
|
|||
|
||||
("-dylib", output_path)
|
||||
}
|
||||
LinkType::None => internal_error!("link_macos should not be called with link type of none"),
|
||||
};
|
||||
|
||||
let arch = match target.architecture {
|
||||
|
|
|
@ -628,6 +628,15 @@ toU16 : Int * -> U16
|
|||
toU32 : Int * -> U32
|
||||
toU64 : Int * -> U64
|
||||
toU128 : Int * -> U128
|
||||
|
||||
## Convert a [Num] to a [F32]. If the given number can't be precisely represented in a [F32],
|
||||
## there will be a loss of precision.
|
||||
toF32 : Num * -> F32
|
||||
|
||||
## Convert a [Num] to a [F64]. If the given number can't be precisely represented in a [F64],
|
||||
## there will be a loss of precision.
|
||||
toF64 : Num * -> F64
|
||||
|
||||
## Convert any [Int] to a specifically-sized [Int], after checking validity.
|
||||
## These are checked bitwise operations,
|
||||
## so if the source number is outside the target range, then these will
|
||||
|
@ -643,6 +652,9 @@ toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
|
|||
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
||||
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
||||
|
||||
toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
|
||||
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
|
||||
|
||||
## Convert a number to a [Str].
|
||||
##
|
||||
## This is the same as calling `Num.format {}` - so for more details on
|
||||
|
@ -765,14 +777,6 @@ toU32 : Int * -> U32
|
|||
toU64 : Int * -> U64
|
||||
toU128 : Int * -> U128
|
||||
|
||||
## Convert a #Num to a #F32. If the given number can't be precisely represented in a #F32,
|
||||
## there will be a loss of precision.
|
||||
toF32 : Num * -> F32
|
||||
|
||||
## Convert a #Num to a #F64. If the given number can't be precisely represented in a #F64,
|
||||
## there will be a loss of precision.
|
||||
toF64 : Num * -> F64
|
||||
|
||||
## Convert a #Num to a #Dec. If the given number can't be precisely represented in a #Dec,
|
||||
## there will be a loss of precision.
|
||||
toDec : Num * -> Dec
|
||||
|
|
|
@ -336,6 +336,9 @@ toU32 : Int * -> U32
|
|||
toU64 : Int * -> U64
|
||||
toU128 : Int * -> U128
|
||||
|
||||
toF32 : Num * -> F32
|
||||
toF64 : Num * -> F64
|
||||
|
||||
toI8Checked : Int * -> Result I8 [ OutOfBounds ]*
|
||||
toI16Checked : Int * -> Result I16 [ OutOfBounds ]*
|
||||
toI32Checked : Int * -> Result I32 [ OutOfBounds ]*
|
||||
|
@ -346,3 +349,5 @@ toU16Checked : Int * -> Result U16 [ OutOfBounds ]*
|
|||
toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
|
||||
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
||||
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
||||
toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
|
||||
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
|
||||
|
|
|
@ -615,7 +615,35 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
add_top_level_function_type!(
|
||||
Symbol::NUM_TO_NAT_CHECKED,
|
||||
vec![int_type(flex(TVAR1))],
|
||||
Box::new(result_type(nat_type(), out_of_bounds)),
|
||||
Box::new(result_type(nat_type(), out_of_bounds.clone())),
|
||||
);
|
||||
|
||||
// toF32 : Num * -> F32
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_TO_F32,
|
||||
vec![num_type(flex(TVAR1))],
|
||||
Box::new(f32_type()),
|
||||
);
|
||||
|
||||
// toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_TO_F32_CHECKED,
|
||||
vec![num_type(flex(TVAR1))],
|
||||
Box::new(result_type(f32_type(), out_of_bounds.clone())),
|
||||
);
|
||||
|
||||
// toF64 : Num * -> F64
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_TO_F64,
|
||||
vec![num_type(flex(TVAR1))],
|
||||
Box::new(f64_type()),
|
||||
);
|
||||
|
||||
// toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_TO_F64_CHECKED,
|
||||
vec![num_type(flex(TVAR1))],
|
||||
Box::new(result_type(f64_type(), out_of_bounds)),
|
||||
);
|
||||
|
||||
// toStr : Num a -> Str
|
||||
|
|
74
compiler/can/src/abilities.rs
Normal file
74
compiler/can/src/abilities.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_types::types::Type;
|
||||
|
||||
use crate::annotation::HasClause;
|
||||
|
||||
/// Stores information about an ability member definition, including the parent ability, the
|
||||
/// defining type, and what type variables need to be instantiated with instances of the ability.
|
||||
#[derive(Debug)]
|
||||
struct AbilityMemberData {
|
||||
#[allow(unused)]
|
||||
parent_ability: Symbol,
|
||||
#[allow(unused)]
|
||||
signature: Type,
|
||||
#[allow(unused)]
|
||||
bound_has_clauses: Vec<HasClause>,
|
||||
}
|
||||
|
||||
/// Stores information about what abilities exist in a scope, what it means to implement an
|
||||
/// ability, and what types implement them.
|
||||
// TODO(abilities): this should probably go on the Scope, I don't put it there for now because we
|
||||
// are only dealing with inter-module abilities for now.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct AbilitiesStore {
|
||||
/// Maps an ability to the members defining it.
|
||||
#[allow(unused)]
|
||||
members_of_ability: MutMap<Symbol, Vec<Symbol>>,
|
||||
|
||||
/// Information about all members composing abilities.
|
||||
ability_members: MutMap<Symbol, AbilityMemberData>,
|
||||
|
||||
/// Tuples of (type, member) specifying that `type` declares an implementation of an ability
|
||||
/// member `member`.
|
||||
#[allow(unused)]
|
||||
declared_implementations: MutSet<(Symbol, Symbol)>,
|
||||
}
|
||||
|
||||
impl AbilitiesStore {
|
||||
pub fn register_ability(
|
||||
&mut self,
|
||||
ability: Symbol,
|
||||
members: Vec<(Symbol, Type, Vec<HasClause>)>,
|
||||
) {
|
||||
let mut members_vec = Vec::with_capacity(members.len());
|
||||
for (member, signature, bound_has_clauses) in members.into_iter() {
|
||||
members_vec.push(member);
|
||||
let old_member = self.ability_members.insert(
|
||||
member,
|
||||
AbilityMemberData {
|
||||
parent_ability: ability,
|
||||
signature,
|
||||
bound_has_clauses,
|
||||
},
|
||||
);
|
||||
debug_assert!(old_member.is_none(), "Replacing existing member definition");
|
||||
}
|
||||
let old_ability = self.members_of_ability.insert(ability, members_vec);
|
||||
debug_assert!(
|
||||
old_ability.is_none(),
|
||||
"Replacing existing ability definition"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register_implementation(&mut self, implementing_type: Symbol, ability_member: Symbol) {
|
||||
let old_impl = self
|
||||
.declared_implementations
|
||||
.insert((implementing_type, ability_member));
|
||||
debug_assert!(!old_impl, "Replacing existing implementation");
|
||||
}
|
||||
|
||||
pub fn is_ability_member_name(&self, name: Symbol) -> bool {
|
||||
self.ability_members.contains_key(&name)
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
use crate::env::Env;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
|
||||
use roc_error_macros::todo_abilities;
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
use roc_parse::ast::{AssignedField, Pattern, Tag, TypeAnnotation, TypeHeader};
|
||||
use roc_parse::ast::{AssignedField, ExtractSpaces, Pattern, Tag, TypeAnnotation, TypeHeader};
|
||||
use roc_problem::can::ShadowKind;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{
|
||||
|
@ -104,6 +104,10 @@ impl IntroducedVariables {
|
|||
.find(|nv| nv.variable == var)
|
||||
.map(|nv| &nv.name)
|
||||
}
|
||||
|
||||
pub fn named_var_by_name(&self, name: &Lowercase) -> Option<&NamedVariable> {
|
||||
self.named.iter().find(|nv| &nv.name == name)
|
||||
}
|
||||
}
|
||||
|
||||
fn malformed(env: &mut Env, region: Region, name: &str) {
|
||||
|
@ -143,6 +147,78 @@ pub fn canonicalize_annotation(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HasClause {
|
||||
pub var_name: Lowercase,
|
||||
pub var: Variable,
|
||||
pub ability: Symbol,
|
||||
}
|
||||
|
||||
pub fn canonicalize_annotation_with_possible_clauses(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
annotation: &TypeAnnotation,
|
||||
region: Region,
|
||||
var_store: &mut VarStore,
|
||||
abilities_in_scope: &[Symbol],
|
||||
) -> (Annotation, Vec<Loc<HasClause>>) {
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
let mut references = MutSet::default();
|
||||
let mut aliases = SendMap::default();
|
||||
|
||||
let (annotation, region, clauses) = match annotation {
|
||||
TypeAnnotation::Where(annotation, clauses) => {
|
||||
let mut can_clauses = Vec::with_capacity(clauses.len());
|
||||
for clause in clauses.iter() {
|
||||
match canonicalize_has_clause(
|
||||
env,
|
||||
scope,
|
||||
var_store,
|
||||
&mut introduced_variables,
|
||||
clause,
|
||||
abilities_in_scope,
|
||||
&mut references,
|
||||
) {
|
||||
Ok(result) => can_clauses.push(Loc::at(clause.region, result)),
|
||||
Err(err_type) => {
|
||||
return (
|
||||
Annotation {
|
||||
typ: err_type,
|
||||
introduced_variables,
|
||||
references,
|
||||
aliases,
|
||||
},
|
||||
can_clauses,
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
(&annotation.value, annotation.region, can_clauses)
|
||||
}
|
||||
annot => (annot, region, vec![]),
|
||||
};
|
||||
|
||||
let typ = can_annotation_help(
|
||||
env,
|
||||
annotation,
|
||||
region,
|
||||
scope,
|
||||
var_store,
|
||||
&mut introduced_variables,
|
||||
&mut aliases,
|
||||
&mut references,
|
||||
);
|
||||
|
||||
let annot = Annotation {
|
||||
typ,
|
||||
introduced_variables,
|
||||
references,
|
||||
aliases,
|
||||
};
|
||||
|
||||
(annot, clauses)
|
||||
}
|
||||
|
||||
fn make_apply_symbol(
|
||||
env: &mut Env,
|
||||
region: Region,
|
||||
|
@ -271,7 +347,13 @@ pub fn find_type_def_symbols(
|
|||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||
stack.push(inner);
|
||||
}
|
||||
Where(..) => todo_abilities!(),
|
||||
Where(annotation, clauses) => {
|
||||
stack.push(&annotation.value);
|
||||
|
||||
for has_clause in clauses.iter() {
|
||||
stack.push(&has_clause.value.ability.value);
|
||||
}
|
||||
}
|
||||
Inferred | Wildcard | Malformed(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -449,9 +531,10 @@ fn can_annotation_help(
|
|||
Err((original_region, shadow, _new_symbol)) => {
|
||||
let problem = Problem::Shadowed(original_region, shadow.clone());
|
||||
|
||||
env.problem(roc_problem::can::Problem::ShadowingInAnnotation {
|
||||
env.problem(roc_problem::can::Problem::Shadowing {
|
||||
original_region,
|
||||
shadow,
|
||||
kind: ShadowKind::Variable,
|
||||
});
|
||||
|
||||
return Type::Erroneous(problem);
|
||||
|
@ -685,7 +768,17 @@ fn can_annotation_help(
|
|||
|
||||
Type::Variable(var)
|
||||
}
|
||||
Where(..) => todo_abilities!(),
|
||||
Where(_annotation, clauses) => {
|
||||
debug_assert!(!clauses.is_empty());
|
||||
|
||||
// Has clauses are allowed only on the top level of an ability member signature (for
|
||||
// now), which we handle elsewhere.
|
||||
env.problem(roc_problem::can::Problem::IllegalHasClause {
|
||||
region: Region::across_all(clauses.iter().map(|clause| &clause.region)),
|
||||
});
|
||||
|
||||
Type::Erroneous(Problem::CanonicalizationProblem)
|
||||
}
|
||||
Malformed(string) => {
|
||||
malformed(env, region, string);
|
||||
|
||||
|
@ -698,6 +791,72 @@ fn can_annotation_help(
|
|||
}
|
||||
}
|
||||
|
||||
fn canonicalize_has_clause(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
clause: &Loc<roc_parse::ast::HasClause<'_>>,
|
||||
abilities_in_scope: &[Symbol],
|
||||
references: &mut MutSet<Symbol>,
|
||||
) -> Result<HasClause, Type> {
|
||||
let Loc {
|
||||
region,
|
||||
value: roc_parse::ast::HasClause { var, ability },
|
||||
} = clause;
|
||||
let region = *region;
|
||||
|
||||
let var_name = var.extract_spaces().item;
|
||||
debug_assert!(
|
||||
var_name.starts_with(char::is_lowercase),
|
||||
"Vars should have been parsed as lowercase"
|
||||
);
|
||||
let var_name = Lowercase::from(var_name);
|
||||
|
||||
let ability = match ability.value {
|
||||
TypeAnnotation::Apply(module_name, ident, _type_arguments) => {
|
||||
let symbol = make_apply_symbol(env, ability.region, scope, module_name, ident)?;
|
||||
if !abilities_in_scope.contains(&symbol) {
|
||||
let region = ability.region;
|
||||
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region });
|
||||
return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region)));
|
||||
}
|
||||
symbol
|
||||
}
|
||||
_ => {
|
||||
let region = ability.region;
|
||||
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region });
|
||||
return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region)));
|
||||
}
|
||||
};
|
||||
|
||||
references.insert(ability);
|
||||
|
||||
if let Some(shadowing) = introduced_variables.named_var_by_name(&var_name) {
|
||||
let var_name_ident = var_name.to_string().into();
|
||||
let shadow = Loc::at(region, var_name_ident);
|
||||
env.problem(roc_problem::can::Problem::Shadowing {
|
||||
original_region: shadowing.first_seen,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
});
|
||||
return Err(Type::Erroneous(Problem::Shadowed(
|
||||
shadowing.first_seen,
|
||||
shadow,
|
||||
)));
|
||||
}
|
||||
|
||||
let var = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named(var_name.clone(), Loc::at(region, var));
|
||||
|
||||
Ok(HasClause {
|
||||
var_name,
|
||||
var,
|
||||
ability,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn can_extension_type<'a>(
|
||||
env: &mut Env,
|
||||
|
|
|
@ -266,6 +266,10 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
NUM_TO_U128_CHECKED => num_to_u128_checked,
|
||||
NUM_TO_NAT => num_to_nat,
|
||||
NUM_TO_NAT_CHECKED => num_to_nat_checked,
|
||||
NUM_TO_F32 => num_to_f32,
|
||||
NUM_TO_F32_CHECKED => num_to_f32_checked,
|
||||
NUM_TO_F64 => num_to_f64,
|
||||
NUM_TO_F64_CHECKED => num_to_f64_checked,
|
||||
NUM_TO_STR => num_to_str,
|
||||
RESULT_MAP => result_map,
|
||||
RESULT_MAP_ERR => result_map_err,
|
||||
|
@ -482,6 +486,18 @@ fn num_to_nat(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||
}
|
||||
|
||||
// Num.toF32 : Num * -> F32
|
||||
fn num_to_f32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
// Defer to NumToFloatCast
|
||||
lowlevel_1(symbol, LowLevel::NumToFloatCast, var_store)
|
||||
}
|
||||
|
||||
// Num.toF64 : Num * -> F64
|
||||
fn num_to_f64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
// Defer to NumToFloatCast
|
||||
lowlevel_1(symbol, LowLevel::NumToFloatCast, var_store)
|
||||
}
|
||||
|
||||
fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) -> Def {
|
||||
let bool_var = var_store.fresh();
|
||||
let num_var_1 = var_store.fresh();
|
||||
|
@ -589,6 +605,8 @@ num_to_checked! {
|
|||
num_to_u64_checked
|
||||
num_to_u128_checked
|
||||
num_to_nat_checked
|
||||
num_to_f32_checked
|
||||
num_to_f64_checked
|
||||
}
|
||||
|
||||
// Num.toStr : Num a -> Str
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
use crate::abilities::AbilitiesStore;
|
||||
use crate::annotation::canonicalize_annotation;
|
||||
use crate::annotation::canonicalize_annotation_with_possible_clauses;
|
||||
use crate::annotation::IntroducedVariables;
|
||||
use crate::env::Env;
|
||||
use crate::expr::ClosureData;
|
||||
use crate::expr::Expr::{self, *};
|
||||
use crate::expr::{canonicalize_expr, local_successors_with_duplicates, Output, Recursive};
|
||||
use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern};
|
||||
use crate::pattern::{bindings_from_patterns, canonicalize_def_header_pattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
use crate::scope::create_alias;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::all::ImSet;
|
||||
use roc_collections::all::{default_hasher, ImEntry, ImMap, MutMap, MutSet, SendMap};
|
||||
use roc_error_macros::todo_abilities;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast;
|
||||
use roc_parse::ast::AbilityMember;
|
||||
use roc_parse::ast::ExtractSpaces;
|
||||
use roc_parse::ast::TypeHeader;
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::ShadowKind;
|
||||
use roc_problem::can::{CycleEntry, Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
|
@ -49,11 +53,12 @@ pub struct CanDefs {
|
|||
pub can_defs_by_symbol: MutMap<Symbol, Def>,
|
||||
pub aliases: SendMap<Symbol, Alias>,
|
||||
}
|
||||
|
||||
/// A Def that has had patterns and type annnotations canonicalized,
|
||||
/// but no Expr canonicalization has happened yet. Also, it has had spaces
|
||||
/// and nesting resolved, and knows whether annotations are standalone or not.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum PendingDef<'a> {
|
||||
enum PendingValueDef<'a> {
|
||||
/// A standalone annotation with no body
|
||||
AnnotationOnly(
|
||||
&'a Loc<ast::Pattern<'a>>,
|
||||
|
@ -73,7 +78,10 @@ enum PendingDef<'a> {
|
|||
&'a Loc<ast::TypeAnnotation<'a>>,
|
||||
&'a Loc<ast::Expr<'a>>,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum PendingTypeDef<'a> {
|
||||
/// A structural or opaque type alias, e.g. `Ints : List Int` or `Age := U32` respectively.
|
||||
Alias {
|
||||
name: Loc<Symbol>,
|
||||
|
@ -82,10 +90,19 @@ enum PendingDef<'a> {
|
|||
kind: AliasKind,
|
||||
},
|
||||
|
||||
Ability {
|
||||
name: Loc<Symbol>,
|
||||
members: &'a [ast::AbilityMember<'a>],
|
||||
},
|
||||
|
||||
/// An invalid alias, that is ignored in the rest of the pipeline
|
||||
/// e.g. a shadowed alias, or a definition like `MyAlias 1 : Int`
|
||||
/// with an incorrect pattern
|
||||
InvalidAlias { kind: AliasKind },
|
||||
|
||||
/// An invalid ability, that is ignored in the rest of the pipeline.
|
||||
/// E.g. a shadowed ability, or with a bad definition.
|
||||
InvalidAbility,
|
||||
}
|
||||
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
|
@ -206,56 +223,53 @@ pub fn canonicalize_defs<'a>(
|
|||
let num_defs = loc_defs.len();
|
||||
let mut refs_by_symbol = MutMap::default();
|
||||
let mut can_defs_by_symbol = HashMap::with_capacity_and_hasher(num_defs, default_hasher());
|
||||
let mut pending = Vec::with_capacity(num_defs); // TODO bump allocate this!
|
||||
|
||||
// Canonicalize all the patterns, record shadowing problems, and store
|
||||
// the ast::Expr values in pending_exprs for further canonicalization
|
||||
// once we've finished assembling the entire scope.
|
||||
let mut type_defs = Vec::with_capacity(num_defs);
|
||||
let mut value_defs = Vec::with_capacity(num_defs);
|
||||
|
||||
for loc_def in loc_defs {
|
||||
match to_pending_def(env, var_store, &loc_def.value, &mut scope, pattern_type) {
|
||||
None => (),
|
||||
Some((new_output, pending_def)) => {
|
||||
// store the top-level defs, used to ensure that closures won't capture them
|
||||
if let PatternType::TopLevelDef = pattern_type {
|
||||
match &pending_def {
|
||||
PendingDef::AnnotationOnly(_, loc_can_pattern, _)
|
||||
| PendingDef::Body(_, loc_can_pattern, _)
|
||||
| PendingDef::TypedBody(_, loc_can_pattern, _, _) => {
|
||||
env.top_level_symbols.extend(
|
||||
bindings_from_patterns(std::iter::once(loc_can_pattern))
|
||||
.iter()
|
||||
.map(|t| t.0),
|
||||
)
|
||||
match loc_def.value.unroll_def() {
|
||||
Ok(type_def) => type_defs.push(Loc::at(loc_def.region, type_def)),
|
||||
Err(value_def) => value_defs.push(Loc::at(loc_def.region, value_def)),
|
||||
}
|
||||
}
|
||||
|
||||
// Type definitions aren't value definitions, so we don't need to do
|
||||
// anything for them here.
|
||||
PendingDef::Alias { .. } | PendingDef::InvalidAlias { .. } => {}
|
||||
}
|
||||
}
|
||||
// Record the ast::Expr for later. We'll do another pass through these
|
||||
// once we have the entire scope assembled. If we were to canonicalize
|
||||
// the exprs right now, they wouldn't have symbols in scope from defs
|
||||
// that get would have gotten added later in the defs list!
|
||||
pending.push(pending_def);
|
||||
// We need to canonicalize all the type defs first.
|
||||
// Clippy is wrong - we do need the collect, otherwise "env" and "scope" are captured for
|
||||
// longer than we'd like.
|
||||
#[allow(clippy::needless_collect)]
|
||||
let pending_type_defs = type_defs
|
||||
.into_iter()
|
||||
.filter_map(|loc_def| {
|
||||
to_pending_type_def(env, loc_def.value, &mut scope).map(|(new_output, pending_def)| {
|
||||
output.union(new_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
pending_def
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
env.home.register_debug_idents(&env.ident_ids);
|
||||
}
|
||||
|
||||
let mut aliases = SendMap::default();
|
||||
let mut value_defs = Vec::new();
|
||||
enum TypeDef<'a> {
|
||||
AliasLike(
|
||||
Loc<Symbol>,
|
||||
Vec<Loc<Lowercase>>,
|
||||
&'a Loc<ast::TypeAnnotation<'a>>,
|
||||
AliasKind,
|
||||
),
|
||||
Ability(Loc<Symbol>, &'a [AbilityMember<'a>]),
|
||||
}
|
||||
|
||||
let mut type_defs = MutMap::default();
|
||||
let mut abilities_in_scope = Vec::new();
|
||||
|
||||
let mut alias_defs = MutMap::default();
|
||||
let mut referenced_type_symbols = MutMap::default();
|
||||
|
||||
for pending_def in pending.into_iter() {
|
||||
for pending_def in pending_type_defs.into_iter() {
|
||||
match pending_def {
|
||||
PendingDef::Alias {
|
||||
PendingTypeDef::Alias {
|
||||
name,
|
||||
vars,
|
||||
ann,
|
||||
|
@ -269,19 +283,55 @@ pub fn canonicalize_defs<'a>(
|
|||
|
||||
referenced_type_symbols.insert(name.value, referenced_symbols);
|
||||
|
||||
alias_defs.insert(name.value, (name, vars, ann, kind));
|
||||
type_defs.insert(name.value, TypeDef::AliasLike(name, vars, ann, kind));
|
||||
}
|
||||
PendingTypeDef::Ability { name, members } => {
|
||||
let mut referenced_symbols = Vec::with_capacity(2);
|
||||
|
||||
for member in members.iter() {
|
||||
// Add the referenced type symbols of each member function. We need to make
|
||||
// sure those are processed first before we resolve the whole ability
|
||||
// definition.
|
||||
referenced_symbols.extend(crate::annotation::find_type_def_symbols(
|
||||
env.home,
|
||||
&mut env.ident_ids,
|
||||
&member.typ.value,
|
||||
));
|
||||
}
|
||||
|
||||
referenced_type_symbols.insert(name.value, referenced_symbols);
|
||||
type_defs.insert(name.value, TypeDef::Ability(name, members));
|
||||
abilities_in_scope.push(name.value);
|
||||
}
|
||||
PendingTypeDef::InvalidAlias { .. } | PendingTypeDef::InvalidAbility { .. } => { /* ignore */
|
||||
}
|
||||
other => value_defs.push(other),
|
||||
}
|
||||
}
|
||||
|
||||
let sorted = sort_type_defs_before_introduction(referenced_type_symbols);
|
||||
let mut aliases = SendMap::default();
|
||||
let mut abilities = MutMap::default();
|
||||
|
||||
for type_name in sorted {
|
||||
let (name, vars, ann, kind) = alias_defs.remove(&type_name).unwrap();
|
||||
|
||||
match type_defs.remove(&type_name).unwrap() {
|
||||
TypeDef::AliasLike(name, vars, ann, kind) => {
|
||||
let symbol = name.value;
|
||||
let can_ann = canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
|
||||
let can_ann =
|
||||
canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
|
||||
|
||||
// Does this alias reference any abilities? For now, we don't permit that.
|
||||
let ability_references = can_ann
|
||||
.references
|
||||
.iter()
|
||||
.filter_map(|&ty_ref| abilities_in_scope.iter().find(|&&name| name == ty_ref))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Some(one_ability_ref) = ability_references.first() {
|
||||
env.problem(Problem::AliasUsesAbility {
|
||||
loc_name: name,
|
||||
ability: **one_ability_ref,
|
||||
});
|
||||
}
|
||||
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
for symbol in can_ann.references {
|
||||
|
@ -358,6 +408,14 @@ pub fn canonicalize_defs<'a>(
|
|||
aliases.insert(symbol, alias.clone());
|
||||
}
|
||||
|
||||
TypeDef::Ability(name, members) => {
|
||||
// For now we enforce that aliases cannot reference abilities, so let's wait to
|
||||
// resolve ability definitions until aliases are resolved and in scope below.
|
||||
abilities.insert(name.value, (name, members));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we know the alias dependency graph, we can try to insert recursion variables
|
||||
// where aliases are recursive tag unions, or detect illegal recursions.
|
||||
let mut aliases = correct_mutual_recursive_type_alias(env, aliases, var_store);
|
||||
|
@ -371,10 +429,139 @@ pub fn canonicalize_defs<'a>(
|
|||
);
|
||||
}
|
||||
|
||||
// Now we can go through and resolve all pending abilities, to add them to scope.
|
||||
let mut abilities_store = AbilitiesStore::default();
|
||||
for (loc_ability_name, members) in abilities.into_values() {
|
||||
let mut can_members = Vec::with_capacity(members.len());
|
||||
|
||||
for member in members {
|
||||
let (member_annot, clauses) = canonicalize_annotation_with_possible_clauses(
|
||||
env,
|
||||
&mut scope,
|
||||
&member.typ.value,
|
||||
member.typ.region,
|
||||
var_store,
|
||||
&abilities_in_scope,
|
||||
);
|
||||
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
for symbol in member_annot.references {
|
||||
output.references.type_lookups.insert(symbol);
|
||||
output.references.referenced_type_defs.insert(symbol);
|
||||
}
|
||||
|
||||
let member_name = member.name.extract_spaces().item;
|
||||
|
||||
let member_sym = match scope.introduce(
|
||||
member_name.into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
member.name.region,
|
||||
) {
|
||||
Ok(sym) => sym,
|
||||
Err((original_region, shadow, _new_symbol)) => {
|
||||
env.problem(roc_problem::can::Problem::Shadowing {
|
||||
original_region,
|
||||
shadow,
|
||||
kind: ShadowKind::Variable,
|
||||
});
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// What variables in the annotation are bound to the parent ability, and what variables
|
||||
// are bound to some other ability?
|
||||
let (variables_bound_to_ability, variables_bound_to_other_abilities): (Vec<_>, Vec<_>) =
|
||||
clauses
|
||||
.into_iter()
|
||||
.partition(|has_clause| has_clause.value.ability == loc_ability_name.value);
|
||||
|
||||
let mut bad_has_clauses = false;
|
||||
|
||||
if variables_bound_to_ability.is_empty() {
|
||||
// There are no variables bound to the parent ability - then this member doesn't
|
||||
// need to be a part of the ability.
|
||||
env.problem(Problem::AbilityMemberMissingHasClause {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
region: member.name.region,
|
||||
});
|
||||
bad_has_clauses = true;
|
||||
}
|
||||
|
||||
if !variables_bound_to_other_abilities.is_empty() {
|
||||
// Disallow variables bound to other abilities, for now.
|
||||
for bad_clause in variables_bound_to_other_abilities.iter() {
|
||||
env.problem(Problem::AbilityMemberBindsExternalAbility {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
region: bad_clause.region,
|
||||
});
|
||||
}
|
||||
bad_has_clauses = true;
|
||||
}
|
||||
|
||||
if bad_has_clauses {
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
|
||||
let has_clauses = variables_bound_to_ability
|
||||
.into_iter()
|
||||
.map(|clause| clause.value)
|
||||
.collect();
|
||||
can_members.push((member_sym, member_annot.typ, has_clauses));
|
||||
}
|
||||
|
||||
// Store what symbols a type must define implementations for to have this ability.
|
||||
abilities_store.register_ability(loc_ability_name.value, can_members);
|
||||
}
|
||||
|
||||
// Now that we have the scope completely assembled, and shadowing resolved,
|
||||
// we're ready to canonicalize any body exprs.
|
||||
for pending_def in value_defs.into_iter() {
|
||||
output = canonicalize_pending_def(
|
||||
|
||||
// Canonicalize all the patterns, record shadowing problems, and store
|
||||
// the ast::Expr values in pending_exprs for further canonicalization
|
||||
// once we've finished assembling the entire scope.
|
||||
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
|
||||
for loc_def in value_defs.into_iter() {
|
||||
match to_pending_value_def(
|
||||
env,
|
||||
var_store,
|
||||
loc_def.value,
|
||||
&mut scope,
|
||||
&abilities_store,
|
||||
pattern_type,
|
||||
) {
|
||||
None => { /* skip */ }
|
||||
Some((new_output, pending_def)) => {
|
||||
// store the top-level defs, used to ensure that closures won't capture them
|
||||
if let PatternType::TopLevelDef = pattern_type {
|
||||
match &pending_def {
|
||||
PendingValueDef::AnnotationOnly(_, loc_can_pattern, _)
|
||||
| PendingValueDef::Body(_, loc_can_pattern, _)
|
||||
| PendingValueDef::TypedBody(_, loc_can_pattern, _, _) => {
|
||||
env.top_level_symbols.extend(
|
||||
bindings_from_patterns(std::iter::once(loc_can_pattern))
|
||||
.iter()
|
||||
.map(|t| t.0),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Record the ast::Expr for later. We'll do another pass through these
|
||||
// once we have the entire scope assembled. If we were to canonicalize
|
||||
// the exprs right now, they wouldn't have symbols in scope from defs
|
||||
// that get would have gotten added later in the defs list!
|
||||
pending_value_defs.push(pending_def);
|
||||
output.union(new_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for pending_def in pending_value_defs.into_iter() {
|
||||
output = canonicalize_pending_value_def(
|
||||
env,
|
||||
pending_def,
|
||||
output,
|
||||
|
@ -833,6 +1020,13 @@ fn pattern_to_vars_by_symbol(
|
|||
vars_by_symbol.insert(*symbol, expr_var);
|
||||
}
|
||||
|
||||
AbilityMemberSpecialization {
|
||||
ident,
|
||||
specializes: _,
|
||||
} => {
|
||||
vars_by_symbol.insert(*ident, expr_var);
|
||||
}
|
||||
|
||||
AppliedTag { arguments, .. } => {
|
||||
for (var, nested) in arguments {
|
||||
pattern_to_vars_by_symbol(vars_by_symbol, &nested.value, *var);
|
||||
|
@ -910,9 +1104,9 @@ fn add_annotation_aliases(
|
|||
// TODO trim down these arguments!
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn canonicalize_pending_def<'a>(
|
||||
fn canonicalize_pending_value_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
pending_def: PendingDef<'a>,
|
||||
pending_def: PendingValueDef<'a>,
|
||||
mut output: Output,
|
||||
scope: &mut Scope,
|
||||
can_defs_by_symbol: &mut MutMap<Symbol, Def>,
|
||||
|
@ -920,7 +1114,7 @@ fn canonicalize_pending_def<'a>(
|
|||
refs_by_symbol: &mut MutMap<Symbol, (Region, References)>,
|
||||
aliases: &mut ImMap<Symbol, Alias>,
|
||||
) -> Output {
|
||||
use PendingDef::*;
|
||||
use PendingValueDef::*;
|
||||
|
||||
// Make types for the body expr, even if we won't end up having a body.
|
||||
let expr_var = var_store.fresh();
|
||||
|
@ -957,6 +1151,7 @@ fn canonicalize_pending_def<'a>(
|
|||
Pattern::Shadowed(region, loc_ident, _new_symbol) => RuntimeError::Shadowing {
|
||||
original_region: *region,
|
||||
shadow: loc_ident.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
},
|
||||
_ => RuntimeError::NoImplementation,
|
||||
};
|
||||
|
@ -1047,11 +1242,6 @@ fn canonicalize_pending_def<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Alias { .. } => unreachable!("Aliases are handled in a separate pass"),
|
||||
|
||||
InvalidAlias { .. } => {
|
||||
// invalid aliases and opaques (shadowed, incorrect patterns) get ignored
|
||||
}
|
||||
TypedBody(_loc_pattern, loc_can_pattern, loc_ann, loc_expr) => {
|
||||
let type_annotation =
|
||||
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
|
||||
|
@ -1446,85 +1636,14 @@ fn closure_recursivity(symbol: Symbol, closures: &MutMap<Symbol, References>) ->
|
|||
Recursive::NotRecursive
|
||||
}
|
||||
|
||||
fn to_pending_def<'a>(
|
||||
fn to_pending_type_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
def: &'a ast::Def<'a>,
|
||||
def: &'a ast::TypeDef<'a>,
|
||||
scope: &mut Scope,
|
||||
pattern_type: PatternType,
|
||||
) -> Option<(Output, PendingDef<'a>)> {
|
||||
use roc_parse::ast::Def::*;
|
||||
) -> Option<(Output, PendingTypeDef<'a>)> {
|
||||
use ast::TypeDef::*;
|
||||
|
||||
match def {
|
||||
Annotation(loc_pattern, loc_ann) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
Some((
|
||||
output,
|
||||
PendingDef::AnnotationOnly(loc_pattern, loc_can_pattern, loc_ann),
|
||||
))
|
||||
}
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
Some((
|
||||
output,
|
||||
PendingDef::Body(loc_pattern, loc_can_pattern, loc_expr),
|
||||
))
|
||||
}
|
||||
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: _,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => {
|
||||
if ann_pattern.value.equivalent(&body_pattern.value) {
|
||||
// NOTE: Pick the body pattern, picking the annotation one is
|
||||
// incorrect in the presence of optional record fields!
|
||||
//
|
||||
// { x, y } : { x : Int, y ? Bool }*
|
||||
// { x, y ? False } = rec
|
||||
Some(pending_typed_body(
|
||||
env,
|
||||
body_pattern,
|
||||
ann_type,
|
||||
body_expr,
|
||||
var_store,
|
||||
scope,
|
||||
pattern_type,
|
||||
))
|
||||
} else {
|
||||
// the pattern of the annotation does not match the pattern of the body direc
|
||||
env.problems.push(Problem::SignatureDefMismatch {
|
||||
annotation_pattern: ann_pattern.region,
|
||||
def_pattern: body_pattern.region,
|
||||
});
|
||||
|
||||
// TODO: Should we instead build some PendingDef::InvalidAnnotatedBody ? This would
|
||||
// remove the `Option` on this function (and be probably more reliable for further
|
||||
// problem/error reporting)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
|
@ -1533,10 +1652,10 @@ fn to_pending_def<'a>(
|
|||
header: TypeHeader { name, vars },
|
||||
typ: ann,
|
||||
} => {
|
||||
let kind = if matches!(def, Alias { .. }) {
|
||||
AliasKind::Structural
|
||||
let (kind, shadow_kind) = if matches!(def, Alias { .. }) {
|
||||
(AliasKind::Structural, ShadowKind::Alias)
|
||||
} else {
|
||||
AliasKind::Opaque
|
||||
(AliasKind::Opaque, ShadowKind::Opaque)
|
||||
};
|
||||
|
||||
let region = Region::span_across(&name.region, &ann.region);
|
||||
|
@ -1571,7 +1690,7 @@ fn to_pending_def<'a>(
|
|||
|
||||
return Some((
|
||||
Output::default(),
|
||||
PendingDef::InvalidAlias { kind },
|
||||
PendingTypeDef::InvalidAlias { kind },
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1582,7 +1701,7 @@ fn to_pending_def<'a>(
|
|||
value: symbol,
|
||||
};
|
||||
|
||||
let pending_def = PendingDef::Alias {
|
||||
let pending_def = PendingTypeDef::Alias {
|
||||
name,
|
||||
vars: can_rigids,
|
||||
ann,
|
||||
|
@ -1593,51 +1712,152 @@ fn to_pending_def<'a>(
|
|||
}
|
||||
|
||||
Err((original_region, loc_shadowed_symbol, _new_symbol)) => {
|
||||
env.problem(Problem::ShadowingInAnnotation {
|
||||
env.problem(Problem::Shadowing {
|
||||
original_region,
|
||||
shadow: loc_shadowed_symbol,
|
||||
kind: shadow_kind,
|
||||
});
|
||||
|
||||
Some((Output::default(), PendingDef::InvalidAlias { kind }))
|
||||
Some((Output::default(), PendingTypeDef::InvalidAlias { kind }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ability { .. } => todo_abilities!(),
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
members,
|
||||
loc_has: _,
|
||||
} => {
|
||||
let name = match scope.introduce_without_shadow_symbol(
|
||||
name.value.into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
name.region,
|
||||
) {
|
||||
Ok(symbol) => Loc::at(name.region, symbol),
|
||||
Err((original_region, shadowed_symbol)) => {
|
||||
env.problem(Problem::Shadowing {
|
||||
original_region,
|
||||
shadow: shadowed_symbol,
|
||||
kind: ShadowKind::Ability,
|
||||
});
|
||||
return Some((Output::default(), PendingTypeDef::InvalidAbility));
|
||||
}
|
||||
};
|
||||
|
||||
Expect(_condition) => todo!(),
|
||||
if !vars.is_empty() {
|
||||
// Disallow ability type arguments, at least for now.
|
||||
let variables_region = Region::across_all(vars.iter().map(|v| &v.region));
|
||||
|
||||
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {
|
||||
to_pending_def(env, var_store, sub_def, scope, pattern_type)
|
||||
env.problem(Problem::AbilityHasTypeVariables {
|
||||
name: name.value,
|
||||
variables_region,
|
||||
});
|
||||
return Some((Output::default(), PendingTypeDef::InvalidAbility));
|
||||
}
|
||||
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
let pending_ability = PendingTypeDef::Ability {
|
||||
name,
|
||||
// We'll handle adding the member symbols later on when we do all value defs.
|
||||
members,
|
||||
};
|
||||
|
||||
Some((Output::default(), pending_ability))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_typed_body<'a>(
|
||||
fn to_pending_value_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
loc_pattern: &'a Loc<ast::Pattern<'a>>,
|
||||
loc_ann: &'a Loc<ast::TypeAnnotation<'a>>,
|
||||
loc_expr: &'a Loc<ast::Expr<'a>>,
|
||||
var_store: &mut VarStore,
|
||||
def: &'a ast::ValueDef<'a>,
|
||||
scope: &mut Scope,
|
||||
abilities_store: &AbilitiesStore,
|
||||
pattern_type: PatternType,
|
||||
) -> (Output, PendingDef<'a>) {
|
||||
) -> Option<(Output, PendingValueDef<'a>)> {
|
||||
use ast::ValueDef::*;
|
||||
|
||||
match def {
|
||||
Annotation(loc_pattern, loc_ann) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_pattern(
|
||||
let (output, loc_can_pattern) = canonicalize_def_header_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
abilities_store,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
(
|
||||
Some((
|
||||
output,
|
||||
PendingDef::TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr),
|
||||
)
|
||||
PendingValueDef::AnnotationOnly(loc_pattern, loc_can_pattern, loc_ann),
|
||||
))
|
||||
}
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_def_header_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
abilities_store,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
Some((
|
||||
output,
|
||||
PendingValueDef::Body(loc_pattern, loc_can_pattern, loc_expr),
|
||||
))
|
||||
}
|
||||
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: _,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => {
|
||||
if ann_pattern.value.equivalent(&body_pattern.value) {
|
||||
// NOTE: Pick the body pattern, picking the annotation one is
|
||||
// incorrect in the presence of optional record fields!
|
||||
//
|
||||
// { x, y } : { x : Int, y ? Bool }*
|
||||
// { x, y ? False } = rec
|
||||
//
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = canonicalize_def_header_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
abilities_store,
|
||||
pattern_type,
|
||||
&body_pattern.value,
|
||||
body_pattern.region,
|
||||
);
|
||||
|
||||
Some((
|
||||
output,
|
||||
PendingValueDef::TypedBody(body_pattern, loc_can_pattern, ann_type, body_expr),
|
||||
))
|
||||
} else {
|
||||
// the pattern of the annotation does not match the pattern of the body direc
|
||||
env.problems.push(Problem::SignatureDefMismatch {
|
||||
annotation_pattern: ann_pattern.region,
|
||||
def_pattern: body_pattern.region,
|
||||
});
|
||||
|
||||
// TODO: Should we instead build some PendingValueDef::InvalidAnnotatedBody ? This would
|
||||
// remove the `Option` on this function (and be probably more reliable for further
|
||||
// problem/error reporting)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Expect(_condition) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Make aliases recursive
|
||||
|
|
|
@ -161,7 +161,6 @@ pub enum Expr {
|
|||
variant_var: Variable,
|
||||
ext_var: Variable,
|
||||
name: TagName,
|
||||
arguments: Vec<(Variable, Loc<Expr>)>,
|
||||
},
|
||||
|
||||
/// A wrapping of an opaque type, like `$Age 21`
|
||||
|
@ -813,7 +812,6 @@ pub fn canonicalize_expr<'a>(
|
|||
(
|
||||
ZeroArgumentTag {
|
||||
name: TagName::Global((*tag).into()),
|
||||
arguments: vec![],
|
||||
variant_var,
|
||||
closure_name: symbol,
|
||||
ext_var,
|
||||
|
@ -831,7 +829,6 @@ pub fn canonicalize_expr<'a>(
|
|||
(
|
||||
ZeroArgumentTag {
|
||||
name: TagName::Private(symbol),
|
||||
arguments: vec![],
|
||||
variant_var,
|
||||
ext_var,
|
||||
closure_name: lambda_set_symbol,
|
||||
|
@ -1560,15 +1557,13 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments,
|
||||
} => {
|
||||
todo!(
|
||||
"Inlining for ZeroArgumentTag with closure_name {:?}, variant_var {:?}, ext_var {:?}, name {:?}, arguments {:?}",
|
||||
"Inlining for ZeroArgumentTag with closure_name {:?}, variant_var {:?}, ext_var {:?}, name {:?}",
|
||||
closure_name,
|
||||
variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod abilities;
|
||||
pub mod annotation;
|
||||
pub mod builtins;
|
||||
pub mod constraint;
|
||||
|
|
|
@ -589,7 +589,8 @@ fn fix_values_captured_in_closure_pattern(
|
|||
| Shadowed(..)
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| OpaqueNotInScope(..) => (),
|
||||
| OpaqueNotInScope(..)
|
||||
| AbilityMemberSpecialization { .. } => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -646,6 +647,7 @@ fn fix_values_captured_in_closure_expr(
|
|||
| Var(_)
|
||||
| EmptyRecord
|
||||
| RuntimeError(_)
|
||||
| ZeroArgumentTag { .. }
|
||||
| Accessor { .. } => {}
|
||||
|
||||
List { loc_elems, .. } => {
|
||||
|
@ -712,7 +714,7 @@ fn fix_values_captured_in_closure_expr(
|
|||
fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols);
|
||||
}
|
||||
|
||||
Tag { arguments, .. } | ZeroArgumentTag { arguments, .. } => {
|
||||
Tag { arguments, .. } => {
|
||||
for (_, loc_arg) in arguments.iter_mut() {
|
||||
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ 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, Def, WhenBranch};
|
||||
use roc_parse::ast::{AssignedField, Def, TypeDef, ValueDef, WhenBranch};
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
||||
// BinOp precedence logic adapted from Gluon by Markus Westerlind
|
||||
|
@ -88,15 +88,21 @@ fn desugar_def_helps<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
|
||||
use roc_parse::ast::Def::*;
|
||||
fn desugar_type_def<'a>(def: &'a TypeDef<'a>) -> TypeDef<'a> {
|
||||
use TypeDef::*;
|
||||
|
||||
match def {
|
||||
Body(loc_pattern, loc_expr) => Body(loc_pattern, desugar_expr(arena, loc_expr)),
|
||||
SpaceBefore(def, _) | SpaceAfter(def, _) => desugar_def(arena, def),
|
||||
alias @ Alias { .. } => *alias,
|
||||
opaque @ Opaque { .. } => *opaque,
|
||||
ability @ Ability { .. } => *ability,
|
||||
}
|
||||
}
|
||||
|
||||
fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a> {
|
||||
use ValueDef::*;
|
||||
|
||||
match def {
|
||||
Body(loc_pattern, loc_expr) => Body(loc_pattern, desugar_expr(arena, loc_expr)),
|
||||
ann @ Annotation(_, _) => *ann,
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
|
@ -115,6 +121,16 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
|
|||
let desugared_condition = &*arena.alloc(desugar_expr(arena, condition));
|
||||
Expect(desugared_condition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
|
||||
use roc_parse::ast::Def::*;
|
||||
|
||||
match def {
|
||||
Type(type_def) => Type(desugar_type_def(type_def)),
|
||||
Value(value_def) => Value(desugar_value_def(arena, value_def)),
|
||||
SpaceBefore(def, _) | SpaceAfter(def, _) => desugar_def(arena, def),
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::abilities::AbilitiesStore;
|
||||
use crate::annotation::freshen_opaque_def;
|
||||
use crate::env::Env;
|
||||
use crate::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output};
|
||||
|
@ -10,7 +11,7 @@ use roc_module::ident::{Ident, Lowercase, TagName};
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{self, StrLiteral, StrSegment};
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError};
|
||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{LambdaSet, Type};
|
||||
|
@ -62,6 +63,17 @@ pub enum Pattern {
|
|||
SingleQuote(char),
|
||||
Underscore,
|
||||
|
||||
/// An identifier that marks a specialization of an ability member.
|
||||
/// For example, given an ability member definition `hash : a -> U64 | a has Hash`,
|
||||
/// there may be the specialization `hash : Bool -> U64`. In this case we generate a
|
||||
/// new symbol for the specialized "hash" identifier.
|
||||
AbilityMemberSpecialization {
|
||||
/// The symbol for this specialization.
|
||||
ident: Symbol,
|
||||
/// The ability name being specialized.
|
||||
specializes: Symbol,
|
||||
},
|
||||
|
||||
// Runtime Exceptions
|
||||
Shadowed(Region, Loc<Ident>, Symbol),
|
||||
OpaqueNotInScope(Loc<Ident>),
|
||||
|
@ -101,6 +113,11 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
|||
symbols.push(*symbol);
|
||||
}
|
||||
|
||||
AbilityMemberSpecialization { ident, specializes } => {
|
||||
symbols.push(*ident);
|
||||
symbols.push(*specializes);
|
||||
}
|
||||
|
||||
AppliedTag { arguments, .. } => {
|
||||
for (_, nested) in arguments {
|
||||
symbols_from_pattern_help(&nested.value, symbols);
|
||||
|
@ -136,6 +153,56 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn canonicalize_def_header_pattern<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
scope: &mut Scope,
|
||||
abilities_store: &AbilitiesStore,
|
||||
pattern_type: PatternType,
|
||||
pattern: &ast::Pattern<'a>,
|
||||
region: Region,
|
||||
) -> (Output, Loc<Pattern>) {
|
||||
use roc_parse::ast::Pattern::*;
|
||||
|
||||
let mut output = Output::default();
|
||||
match pattern {
|
||||
// Identifiers that shadow ability members may appear (and may only appear) at the header of a def.
|
||||
Identifier(name) => match scope.introduce_or_shadow_ability_member(
|
||||
(*name).into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
region,
|
||||
abilities_store,
|
||||
) {
|
||||
Ok((symbol, shadowing_ability_member)) => {
|
||||
output.references.bound_symbols.insert(symbol);
|
||||
let can_pattern = match shadowing_ability_member {
|
||||
// A fresh identifier.
|
||||
None => Pattern::Identifier(symbol),
|
||||
// Likely a specialization of an ability.
|
||||
Some(ability_member_name) => Pattern::AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: ability_member_name,
|
||||
},
|
||||
};
|
||||
(output, Loc::at(region, can_pattern))
|
||||
}
|
||||
Err((original_region, shadow, new_symbol)) => {
|
||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}));
|
||||
output.references.bound_symbols.insert(new_symbol);
|
||||
|
||||
let can_pattern = Pattern::Shadowed(original_region, shadow, new_symbol);
|
||||
(output, Loc::at(region, can_pattern))
|
||||
}
|
||||
},
|
||||
_ => canonicalize_pattern(env, var_store, scope, pattern_type, pattern, region),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn canonicalize_pattern<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
|
@ -164,6 +231,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}));
|
||||
output.references.bound_symbols.insert(new_symbol);
|
||||
|
||||
|
@ -412,6 +480,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}));
|
||||
|
||||
// No matter what the other patterns
|
||||
|
@ -484,6 +553,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||
original_region,
|
||||
shadow: shadow.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}));
|
||||
|
||||
// No matter what the other patterns
|
||||
|
@ -594,7 +664,12 @@ fn add_bindings_from_patterns(
|
|||
use Pattern::*;
|
||||
|
||||
match pattern {
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
Identifier(symbol)
|
||||
| Shadowed(_, _, symbol)
|
||||
| AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: _,
|
||||
} => {
|
||||
answer.push((*symbol, *region));
|
||||
}
|
||||
AppliedTag {
|
||||
|
|
|
@ -6,6 +6,8 @@ use roc_region::all::{Loc, Region};
|
|||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{Alias, AliasKind, Type};
|
||||
|
||||
use crate::abilities::AbilitiesStore;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Scope {
|
||||
/// All the identifiers in scope, mapped to were they were defined and
|
||||
|
@ -19,6 +21,9 @@ pub struct Scope {
|
|||
/// The type aliases currently in scope
|
||||
pub aliases: SendMap<Symbol, Alias>,
|
||||
|
||||
/// The abilities currently in scope, and their implementors.
|
||||
pub abilities: SendMap<Symbol, Region>,
|
||||
|
||||
/// The current module being processed. This will be used to turn
|
||||
/// unqualified idents into Symbols.
|
||||
home: ModuleId,
|
||||
|
@ -62,6 +67,8 @@ impl Scope {
|
|||
idents: Symbol::default_in_scope(),
|
||||
symbols: SendMap::default(),
|
||||
aliases,
|
||||
// TODO(abilities): default abilities in scope
|
||||
abilities: SendMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,6 +183,11 @@ impl Scope {
|
|||
///
|
||||
/// Returns Err if this would shadow an existing ident, including the
|
||||
/// Symbol and Region of the ident we already had in scope under that name.
|
||||
///
|
||||
/// If this ident shadows an existing one, a new ident is allocated for the shadow. This is
|
||||
/// done so that all identifiers have unique symbols, which is important in particular when
|
||||
/// we generate code for value identifiers.
|
||||
/// If this behavior is undesirable, use [`Self::introduce_without_shadow_symbol`].
|
||||
pub fn introduce(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
|
@ -198,7 +210,82 @@ impl Scope {
|
|||
|
||||
Err((original_region, shadow, symbol))
|
||||
}
|
||||
None => Ok(self.commit_introduction(ident, exposed_ident_ids, all_ident_ids, region)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [Self::introduce], but does not introduce a new symbol for the shadowing symbol.
|
||||
pub fn introduce_without_shadow_symbol(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
exposed_ident_ids: &IdentIds,
|
||||
all_ident_ids: &mut IdentIds,
|
||||
region: Region,
|
||||
) -> Result<Symbol, (Region, Loc<Ident>)> {
|
||||
match self.idents.get(&ident) {
|
||||
Some(&(_, original_region)) => {
|
||||
let shadow = Loc {
|
||||
value: ident.clone(),
|
||||
region,
|
||||
};
|
||||
Err((original_region, shadow))
|
||||
}
|
||||
None => Ok(self.commit_introduction(ident, exposed_ident_ids, all_ident_ids, region)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [Self::introduce], but handles the case of when an ident matches an ability member
|
||||
/// name. In such cases a new symbol is created for the ident (since it's expected to be a
|
||||
/// specialization of the ability member), but the ident is not added to the ident->symbol map.
|
||||
///
|
||||
/// If the ident does not match an ability name, the behavior of this function is exactly that
|
||||
/// of `introduce`.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn introduce_or_shadow_ability_member(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
exposed_ident_ids: &IdentIds,
|
||||
all_ident_ids: &mut IdentIds,
|
||||
region: Region,
|
||||
abilities_store: &AbilitiesStore,
|
||||
) -> Result<(Symbol, Option<Symbol>), (Region, Loc<Ident>, Symbol)> {
|
||||
match self.idents.get(&ident) {
|
||||
Some(&(original_symbol, original_region)) => {
|
||||
let shadow_ident_id = all_ident_ids.add(ident.clone());
|
||||
let shadow_symbol = Symbol::new(self.home, shadow_ident_id);
|
||||
|
||||
self.symbols.insert(shadow_symbol, region);
|
||||
|
||||
if abilities_store.is_ability_member_name(original_symbol) {
|
||||
// Add a symbol for the shadow, but don't re-associate the member name.
|
||||
Ok((shadow_symbol, Some(original_symbol)))
|
||||
} else {
|
||||
// This is an illegal shadow.
|
||||
let shadow = Loc {
|
||||
value: ident.clone(),
|
||||
region,
|
||||
};
|
||||
|
||||
self.idents.insert(ident, (shadow_symbol, region));
|
||||
|
||||
Err((original_region, shadow, shadow_symbol))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let new_symbol =
|
||||
self.commit_introduction(ident, exposed_ident_ids, all_ident_ids, region);
|
||||
Ok((new_symbol, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_introduction(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
exposed_ident_ids: &IdentIds,
|
||||
all_ident_ids: &mut IdentIds,
|
||||
region: Region,
|
||||
) -> Symbol {
|
||||
// If this IdentId was already added previously
|
||||
// when the value was exposed in the module header,
|
||||
// use that existing IdentId. Otherwise, create a fresh one.
|
||||
|
@ -212,9 +299,7 @@ impl Scope {
|
|||
self.symbols.insert(symbol, region);
|
||||
self.idents.insert(ident, (symbol, region));
|
||||
|
||||
Ok(symbol)
|
||||
}
|
||||
}
|
||||
symbol
|
||||
}
|
||||
|
||||
/// Ignore an identifier.
|
||||
|
|
|
@ -881,7 +881,9 @@ pub fn constrain_expr(
|
|||
name,
|
||||
arguments,
|
||||
} => {
|
||||
let mut vars = Vec::with_capacity(arguments.len());
|
||||
// +2 because we push all the arguments, plus variant_var and ext_var
|
||||
let num_vars = arguments.len() + 2;
|
||||
let mut vars = Vec::with_capacity(num_vars);
|
||||
let mut types = Vec::with_capacity(arguments.len());
|
||||
let mut arg_cons = Vec::with_capacity(arguments.len());
|
||||
|
||||
|
@ -923,27 +925,8 @@ pub fn constrain_expr(
|
|||
variant_var,
|
||||
ext_var,
|
||||
name,
|
||||
arguments,
|
||||
closure_name,
|
||||
} => {
|
||||
let mut vars = Vec::with_capacity(arguments.len());
|
||||
let mut types = Vec::with_capacity(arguments.len());
|
||||
let mut arg_cons = Vec::with_capacity(arguments.len());
|
||||
|
||||
for (var, loc_expr) in arguments {
|
||||
let arg_con = constrain_expr(
|
||||
constraints,
|
||||
env,
|
||||
loc_expr.region,
|
||||
&loc_expr.value,
|
||||
Expected::NoExpectation(Type::Variable(*var)),
|
||||
);
|
||||
|
||||
arg_cons.push(arg_con);
|
||||
vars.push(*var);
|
||||
types.push(Type::Variable(*var));
|
||||
}
|
||||
|
||||
let union_con = constraints.equal_types_with_storage(
|
||||
Type::FunctionOrTagUnion(
|
||||
name.clone(),
|
||||
|
@ -953,19 +936,14 @@ pub fn constrain_expr(
|
|||
expected.clone(),
|
||||
Category::TagApply {
|
||||
tag_name: name.clone(),
|
||||
args_count: arguments.len(),
|
||||
args_count: 0,
|
||||
},
|
||||
region,
|
||||
*variant_var,
|
||||
);
|
||||
|
||||
vars.push(*variant_var);
|
||||
vars.push(*ext_var);
|
||||
arg_cons.push(union_con);
|
||||
|
||||
constraints.exists_many(vars, arg_cons)
|
||||
constraints.exists_many([*variant_var, *ext_var], [union_con])
|
||||
}
|
||||
|
||||
OpaqueRef {
|
||||
opaque_var,
|
||||
name,
|
||||
|
|
|
@ -50,7 +50,13 @@ fn headers_from_annotation_help(
|
|||
headers: &mut SendMap<Symbol, Loc<Type>>,
|
||||
) -> bool {
|
||||
match pattern {
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
Identifier(symbol)
|
||||
| Shadowed(_, _, symbol)
|
||||
// TODO(abilities): handle linking the member def to the specialization ident
|
||||
| AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: _,
|
||||
} => {
|
||||
let typ = Loc::at(annotation.region, annotation.value.clone());
|
||||
headers.insert(*symbol, typ);
|
||||
true
|
||||
|
@ -182,7 +188,12 @@ pub fn constrain_pattern(
|
|||
// Erroneous patterns don't add any constraints.
|
||||
}
|
||||
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
Identifier(symbol) | Shadowed(_, _, symbol)
|
||||
// TODO(abilities): handle linking the member def to the specialization ident
|
||||
| AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes: _,
|
||||
} => {
|
||||
if could_be_a_tag_union(expected.get_type_ref()) {
|
||||
state
|
||||
.constraints
|
||||
|
|
|
@ -2,27 +2,35 @@ use crate::annotation::{Formattable, Newlines, Parens};
|
|||
use crate::pattern::fmt_pattern;
|
||||
use crate::spaces::{fmt_spaces, INDENT};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{AbilityDemand, Def, Expr, ExtractSpaces, Pattern, TypeHeader};
|
||||
use roc_parse::ast::{AbilityMember, Def, Expr, ExtractSpaces, Pattern, TypeHeader};
|
||||
use roc_region::all::Loc;
|
||||
|
||||
/// A Located formattable value is also formattable
|
||||
impl<'a> Formattable for Def<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use roc_parse::ast::Def::*;
|
||||
use roc_parse::ast::TypeDef::*;
|
||||
use roc_parse::ast::ValueDef::*;
|
||||
|
||||
match self {
|
||||
Type(def) => match def {
|
||||
Alias { ann, .. } => ann.is_multiline(),
|
||||
Opaque { typ, .. } => typ.is_multiline(),
|
||||
Ability { members, .. } => members.iter().any(|d| d.is_multiline()),
|
||||
},
|
||||
Value(def) => match def {
|
||||
Annotation(loc_pattern, loc_annotation) => {
|
||||
loc_pattern.is_multiline() || loc_annotation.is_multiline()
|
||||
}
|
||||
Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(),
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
loc_pattern.is_multiline() || loc_expr.is_multiline()
|
||||
}
|
||||
AnnotatedBody { .. } => true,
|
||||
Expect(loc_expr) => loc_expr.is_multiline(),
|
||||
},
|
||||
SpaceBefore(sub_def, spaces) | SpaceAfter(sub_def, spaces) => {
|
||||
spaces.iter().any(|s| s.is_comment()) || sub_def.is_multiline()
|
||||
}
|
||||
Ability { demands, .. } => demands.iter().any(|d| d.is_multiline()),
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +43,64 @@ impl<'a> Formattable for Def<'a> {
|
|||
indent: u16,
|
||||
) {
|
||||
use roc_parse::ast::Def::*;
|
||||
use roc_parse::ast::TypeDef::*;
|
||||
use roc_parse::ast::ValueDef::*;
|
||||
|
||||
match self {
|
||||
Type(def) => match def {
|
||||
Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
}
|
||||
| Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
typ: ann,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(name.value);
|
||||
|
||||
for var in *vars {
|
||||
buf.spaces(1);
|
||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||
}
|
||||
|
||||
buf.push_str(match def {
|
||||
Alias { .. } => " :",
|
||||
Opaque { .. } => " :=",
|
||||
_ => unreachable!(),
|
||||
});
|
||||
buf.spaces(1);
|
||||
|
||||
ann.format(buf, indent + INDENT)
|
||||
}
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_has: _,
|
||||
members,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(name.value);
|
||||
for var in *vars {
|
||||
buf.spaces(1);
|
||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||
}
|
||||
|
||||
buf.push_str(" has");
|
||||
|
||||
if !self.is_multiline() {
|
||||
debug_assert_eq!(members.len(), 1);
|
||||
buf.push_str(" ");
|
||||
members[0].format(buf, indent + INDENT);
|
||||
} else {
|
||||
for demand in members.iter() {
|
||||
buf.newline();
|
||||
buf.indent(indent + INDENT);
|
||||
demand.format(buf, indent + INDENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Value(def) => match def {
|
||||
Annotation(loc_pattern, loc_annotation) => {
|
||||
loc_pattern.format(buf, indent);
|
||||
if loc_annotation.is_multiline() {
|
||||
|
@ -59,57 +123,6 @@ impl<'a> Formattable for Def<'a> {
|
|||
);
|
||||
}
|
||||
}
|
||||
Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
}
|
||||
| Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
typ: ann,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(name.value);
|
||||
|
||||
for var in *vars {
|
||||
buf.spaces(1);
|
||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||
}
|
||||
|
||||
buf.push_str(match self {
|
||||
Alias { .. } => " :",
|
||||
Opaque { .. } => " :=",
|
||||
_ => unreachable!(),
|
||||
});
|
||||
buf.spaces(1);
|
||||
|
||||
ann.format(buf, indent + INDENT)
|
||||
}
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_has: _,
|
||||
demands,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(name.value);
|
||||
for var in *vars {
|
||||
buf.spaces(1);
|
||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||
}
|
||||
|
||||
buf.push_str(" has");
|
||||
|
||||
if !self.is_multiline() {
|
||||
debug_assert_eq!(demands.len(), 1);
|
||||
buf.push_str(" ");
|
||||
demands[0].format(buf, indent + INDENT);
|
||||
} else {
|
||||
for demand in demands.iter() {
|
||||
buf.newline();
|
||||
buf.indent(indent + INDENT);
|
||||
demand.format(buf, indent + INDENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
|
||||
}
|
||||
|
@ -133,6 +146,7 @@ impl<'a> Formattable for Def<'a> {
|
|||
buf.newline();
|
||||
fmt_body(buf, &body_pattern.value, &body_expr.value, indent);
|
||||
}
|
||||
},
|
||||
|
||||
SpaceBefore(sub_def, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
|
@ -195,7 +209,7 @@ pub fn fmt_body<'a, 'buf>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for AbilityDemand<'a> {
|
||||
impl<'a> Formattable for AbilityMember<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
self.name.value.is_multiline() || self.typ.is_multiline()
|
||||
}
|
||||
|
|
|
@ -5878,6 +5878,48 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
.build_int_cast_sign_flag(arg, to, to_signed, "inc_cast")
|
||||
.into()
|
||||
}
|
||||
NumToFloatCast => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let (arg, arg_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||
|
||||
match arg_layout {
|
||||
Layout::Builtin(Builtin::Int(width)) => {
|
||||
// Converting from int to float
|
||||
let int_val = arg.into_int_value();
|
||||
let dest = basic_type_from_layout(env, layout).into_float_type();
|
||||
|
||||
if width.is_signed() {
|
||||
env.builder
|
||||
.build_signed_int_to_float(int_val, dest, "signed_int_to_float")
|
||||
.into()
|
||||
} else {
|
||||
env.builder
|
||||
.build_unsigned_int_to_float(int_val, dest, "unsigned_int_to_float")
|
||||
.into()
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(_)) => {
|
||||
// Converting from float to float - e.g. F64 to F32, or vice versa
|
||||
let dest = basic_type_from_layout(env, layout).into_float_type();
|
||||
|
||||
env.builder
|
||||
.build_float_cast(arg.into_float_value(), dest, "cast_float_to_float")
|
||||
.into()
|
||||
}
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
todo!("Support converting Dec values to floats.");
|
||||
}
|
||||
other => {
|
||||
unreachable!("Tried to do a float cast to non-float layout {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
NumToFloatChecked => {
|
||||
// NOTE: There's a NumToIntChecked implementation above,
|
||||
// which could be useful to look at when implementing this.
|
||||
todo!("implement checked float conversion");
|
||||
}
|
||||
Eq => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use bumpalo::{self, collections::Vec};
|
|||
use std::fmt::Write;
|
||||
|
||||
use code_builder::Align;
|
||||
use roc_builtins::bitcode::IntWidth;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
||||
|
@ -17,10 +17,10 @@ use roc_error_macros::internal_error;
|
|||
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
||||
|
||||
use crate::layout::{CallConv, ReturnMethod, WasmLayout};
|
||||
use crate::low_level::LowLevelCall;
|
||||
use crate::low_level::{call_higher_order_lowlevel, LowLevelCall};
|
||||
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
||||
use crate::wasm_module::linking::{DataSymbol, LinkingSegment, WasmObjectSymbol};
|
||||
use crate::wasm_module::sections::{DataMode, DataSegment};
|
||||
use crate::wasm_module::sections::{DataMode, DataSegment, Limits};
|
||||
use crate::wasm_module::{
|
||||
code_builder, CodeBuilder, ExportType, LocalId, Signature, SymInfo, ValueType, WasmModule,
|
||||
};
|
||||
|
@ -29,6 +29,22 @@ use crate::{
|
|||
PTR_TYPE, TARGET_INFO,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ProcSource {
|
||||
Roc,
|
||||
Helper,
|
||||
/// Wrapper function for higher-order calls from Zig to Roc
|
||||
HigherOrderWrapper(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProcLookupData<'a> {
|
||||
pub name: Symbol,
|
||||
pub layout: ProcLayout<'a>,
|
||||
pub linker_index: u32,
|
||||
pub source: ProcSource,
|
||||
}
|
||||
|
||||
pub struct WasmBackend<'a> {
|
||||
pub env: &'a Env<'a>,
|
||||
interns: &'a mut Interns,
|
||||
|
@ -37,9 +53,9 @@ pub struct WasmBackend<'a> {
|
|||
module: WasmModule<'a>,
|
||||
layout_ids: LayoutIds<'a>,
|
||||
next_constant_addr: u32,
|
||||
fn_index_offset: u32,
|
||||
pub fn_index_offset: u32,
|
||||
called_preload_fns: Vec<'a, u32>,
|
||||
proc_lookup: Vec<'a, (Symbol, ProcLayout<'a>, u32)>,
|
||||
pub proc_lookup: Vec<'a, ProcLookupData<'a>>,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
|
||||
// Function-level data
|
||||
|
@ -56,7 +72,7 @@ impl<'a> WasmBackend<'a> {
|
|||
env: &'a Env<'a>,
|
||||
interns: &'a mut Interns,
|
||||
layout_ids: LayoutIds<'a>,
|
||||
proc_lookup: Vec<'a, (Symbol, ProcLayout<'a>, u32)>,
|
||||
proc_lookup: Vec<'a, ProcLookupData<'a>>,
|
||||
mut module: WasmModule<'a>,
|
||||
fn_index_offset: u32,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
|
@ -109,31 +125,45 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_helpers(&mut self) -> Vec<'a, Proc<'a>> {
|
||||
pub fn get_helpers(&mut self) -> Vec<'a, Proc<'a>> {
|
||||
self.helper_proc_gen.take_procs()
|
||||
}
|
||||
|
||||
fn register_helper_proc(&mut self, new_proc_info: (Symbol, ProcLayout<'a>)) {
|
||||
let (new_proc_sym, new_proc_layout) = new_proc_info;
|
||||
let wasm_fn_index = self.proc_lookup.len() as u32;
|
||||
pub fn register_helper_proc(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
layout: ProcLayout<'a>,
|
||||
source: ProcSource,
|
||||
) -> u32 {
|
||||
let proc_index = self.proc_lookup.len();
|
||||
let wasm_fn_index = self.fn_index_offset + proc_index as u32;
|
||||
let linker_sym_index = self.module.linking.symbol_table.len() as u32;
|
||||
|
||||
let name = self
|
||||
.layout_ids
|
||||
.get_toplevel(new_proc_sym, &new_proc_layout)
|
||||
.to_symbol_string(new_proc_sym, self.interns);
|
||||
.get_toplevel(symbol, &layout)
|
||||
.to_symbol_string(symbol, self.interns);
|
||||
|
||||
self.proc_lookup.push(ProcLookupData {
|
||||
name: symbol,
|
||||
layout,
|
||||
linker_index: linker_sym_index,
|
||||
source,
|
||||
});
|
||||
|
||||
self.proc_lookup
|
||||
.push((new_proc_sym, new_proc_layout, linker_sym_index));
|
||||
let linker_symbol = SymInfo::Function(WasmObjectSymbol::Defined {
|
||||
flags: 0,
|
||||
index: wasm_fn_index,
|
||||
name,
|
||||
});
|
||||
self.module.linking.symbol_table.push(linker_symbol);
|
||||
|
||||
wasm_fn_index
|
||||
}
|
||||
|
||||
pub fn finalize(self) -> (WasmModule<'a>, Vec<'a, u32>) {
|
||||
pub fn finalize(mut self) -> (WasmModule<'a>, Vec<'a, u32>) {
|
||||
let fn_table_size = 1 + self.module.element.max_table_index();
|
||||
self.module.table.function_table.limits = Limits::MinMax(fn_table_size, fn_table_size);
|
||||
(self.module, self.called_preload_fns)
|
||||
}
|
||||
|
||||
|
@ -150,8 +180,12 @@ impl<'a> WasmBackend<'a> {
|
|||
#[cfg(not(debug_assertions))]
|
||||
pub fn register_symbol_debug_names(&self) {}
|
||||
|
||||
pub fn get_fn_table_index(&mut self, fn_index: u32) -> i32 {
|
||||
self.module.element.get_fn_table_index(fn_index)
|
||||
}
|
||||
|
||||
/// Create an IR Symbol for an anonymous value (such as ListLiteral)
|
||||
fn create_symbol(&mut self, debug_name: &str) -> Symbol {
|
||||
pub fn create_symbol(&mut self, debug_name: &str) -> Symbol {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
|
@ -204,7 +238,7 @@ impl<'a> WasmBackend<'a> {
|
|||
let ret_layout = WasmLayout::new(&proc.ret_layout);
|
||||
|
||||
let ret_type = match ret_layout.return_method(CallConv::C) {
|
||||
Primitive(ty) => Some(ty),
|
||||
Primitive(ty, _) => Some(ty),
|
||||
NoReturnValue => None,
|
||||
WriteToPointerArg => {
|
||||
self.storage.arg_types.push(PTR_TYPE);
|
||||
|
@ -251,20 +285,156 @@ impl<'a> WasmBackend<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
fn append_proc_debug_name(&mut self, name: Symbol) {
|
||||
fn append_proc_debug_name(&mut self, sym: Symbol) {
|
||||
let proc_index = self
|
||||
.proc_lookup
|
||||
.iter()
|
||||
.position(|(n, _, _)| *n == name)
|
||||
.position(|ProcLookupData { name, .. }| *name == sym)
|
||||
.unwrap();
|
||||
let wasm_fn_index = self.fn_index_offset + proc_index as u32;
|
||||
|
||||
let mut debug_name = bumpalo::collections::String::with_capacity_in(64, self.env.arena);
|
||||
write!(debug_name, "{:?}", name).unwrap();
|
||||
write!(debug_name, "{:?}", sym).unwrap();
|
||||
let name_bytes = debug_name.into_bytes().into_bump_slice();
|
||||
self.module.names.append_function(wasm_fn_index, name_bytes);
|
||||
}
|
||||
|
||||
/// Build a wrapper around a Roc procedure so that it can be called from our higher-order Zig builtins.
|
||||
///
|
||||
/// The generic Zig code passes *pointers* to all of the argument values (e.g. on the heap in a List).
|
||||
/// Numbers up to 64 bits are passed by value, so we need to load them from the provided pointer.
|
||||
/// Everything else is passed by reference, so we can just pass the pointer through.
|
||||
///
|
||||
/// NOTE: If the builtins expected the return pointer first and closure data last, we could eliminate the wrapper
|
||||
/// when all args are pass-by-reference and non-zero size. But currently we need it to swap those around.
|
||||
pub fn build_higher_order_wrapper(
|
||||
&mut self,
|
||||
wrapper_lookup_idx: usize,
|
||||
inner_lookup_idx: usize,
|
||||
) {
|
||||
use Align::*;
|
||||
use ValueType::*;
|
||||
|
||||
let ProcLookupData {
|
||||
name: wrapper_name,
|
||||
layout: wrapper_proc_layout,
|
||||
..
|
||||
} = self.proc_lookup[wrapper_lookup_idx];
|
||||
let wrapper_arg_layouts = wrapper_proc_layout.arguments;
|
||||
|
||||
// Our convention is that the last arg of the wrapper is the heap return pointer
|
||||
let heap_return_ptr_id = LocalId(wrapper_arg_layouts.len() as u32 - 1);
|
||||
let inner_ret_layout = match wrapper_arg_layouts.last() {
|
||||
Some(Layout::Boxed(inner)) => WasmLayout::new(inner),
|
||||
x => internal_error!("Higher-order wrapper: invalid return layout {:?}", x),
|
||||
};
|
||||
|
||||
let mut n_inner_wasm_args = 0;
|
||||
let ret_type_and_size = match inner_ret_layout.return_method(CallConv::C) {
|
||||
ReturnMethod::NoReturnValue => None,
|
||||
ReturnMethod::Primitive(ty, size) => {
|
||||
// If the inner function returns a primitive, load the address to store it at
|
||||
// After the call, it will be under the call result in the value stack
|
||||
self.code_builder.get_local(heap_return_ptr_id);
|
||||
Some((ty, size))
|
||||
}
|
||||
ReturnMethod::WriteToPointerArg => {
|
||||
// If the inner function writes to a return pointer, load its address
|
||||
self.code_builder.get_local(heap_return_ptr_id);
|
||||
n_inner_wasm_args += 1;
|
||||
None
|
||||
}
|
||||
x => internal_error!("A Roc function should never use ReturnMethod {:?}", x),
|
||||
};
|
||||
|
||||
// Load all the arguments for the inner function
|
||||
for (i, wrapper_arg) in wrapper_arg_layouts.iter().enumerate() {
|
||||
let is_closure_data = i == 0; // Skip closure data (first for wrapper, last for inner)
|
||||
let is_return_pointer = i == wrapper_arg_layouts.len() - 1; // Skip return pointer (may not be an arg for inner. And if it is, swaps from end to start)
|
||||
if is_closure_data || is_return_pointer || wrapper_arg.stack_size(TARGET_INFO) == 0 {
|
||||
continue;
|
||||
}
|
||||
n_inner_wasm_args += 1;
|
||||
|
||||
// Load wrapper argument. They're all pointers.
|
||||
self.code_builder.get_local(LocalId(i as u32));
|
||||
|
||||
// Dereference any primitive-valued arguments
|
||||
match wrapper_arg {
|
||||
Layout::Boxed(inner_arg) => match inner_arg {
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U8 | IntWidth::I8)) => {
|
||||
self.code_builder.i32_load8_u(Bytes1, 0);
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U16 | IntWidth::I16)) => {
|
||||
self.code_builder.i32_load16_u(Bytes2, 0);
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U32 | IntWidth::I32)) => {
|
||||
self.code_builder.i32_load(Bytes4, 0);
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(IntWidth::U64 | IntWidth::I64)) => {
|
||||
self.code_builder.i64_load(Bytes8, 0);
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F32)) => {
|
||||
self.code_builder.f32_load(Bytes4, 0);
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
|
||||
self.code_builder.f64_load(Bytes8, 0);
|
||||
}
|
||||
Layout::Builtin(Builtin::Bool) => {
|
||||
self.code_builder.i32_load8_u(Bytes1, 0);
|
||||
}
|
||||
_ => {
|
||||
// Any other layout is a pointer, which we've already loaded. Nothing to do!
|
||||
}
|
||||
},
|
||||
x => internal_error!("Higher-order wrapper: expected a Box layout, got {:?}", x),
|
||||
}
|
||||
}
|
||||
|
||||
// If the inner function has closure data, it's the last arg of the inner fn
|
||||
let closure_data_layout = wrapper_arg_layouts[0];
|
||||
if closure_data_layout.stack_size(TARGET_INFO) > 0 {
|
||||
self.code_builder.get_local(LocalId(0));
|
||||
}
|
||||
|
||||
// Call the wrapped inner function
|
||||
let lookup = &self.proc_lookup[inner_lookup_idx];
|
||||
let inner_wasm_fn_index = self.fn_index_offset + inner_lookup_idx as u32;
|
||||
let has_return_val = ret_type_and_size.is_some();
|
||||
self.code_builder.call(
|
||||
inner_wasm_fn_index,
|
||||
lookup.linker_index,
|
||||
n_inner_wasm_args,
|
||||
has_return_val,
|
||||
);
|
||||
|
||||
// If the inner function returns a primitive, store it to the address we loaded at the very beginning
|
||||
if let Some((ty, size)) = ret_type_and_size {
|
||||
match (ty, size) {
|
||||
(I64, 8) => self.code_builder.i64_store(Bytes8, 0),
|
||||
(I32, 4) => self.code_builder.i32_store(Bytes4, 0),
|
||||
(I32, 2) => self.code_builder.i32_store16(Bytes2, 0),
|
||||
(I32, 1) => self.code_builder.i32_store8(Bytes1, 0),
|
||||
(F32, 4) => self.code_builder.f32_store(Bytes4, 0),
|
||||
(F64, 8) => self.code_builder.f64_store(Bytes8, 0),
|
||||
_ => {
|
||||
internal_error!("Cannot store {:?} with alignment of {:?}", ty, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write empty function header (local variables array with zero length)
|
||||
self.code_builder.build_fn_header_and_footer(&[], 0, None);
|
||||
|
||||
self.module.add_function_signature(Signature {
|
||||
param_types: bumpalo::vec![in self.env.arena; I32; wrapper_arg_layouts.len()],
|
||||
ret_type: None,
|
||||
});
|
||||
|
||||
self.append_proc_debug_name(wrapper_name);
|
||||
self.reset();
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
|
||||
STATEMENTS
|
||||
|
@ -551,8 +721,8 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec);
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
self.stmt(rc_stmt);
|
||||
|
@ -827,11 +997,16 @@ impl<'a> WasmBackend<'a> {
|
|||
ret_storage,
|
||||
)
|
||||
}
|
||||
|
||||
CallType::LowLevel { op: lowlevel, .. } => {
|
||||
self.expr_call_low_level(*lowlevel, arguments, ret_sym, ret_layout, ret_storage)
|
||||
}
|
||||
|
||||
x => todo!("call type {:?}", x),
|
||||
CallType::HigherOrder(higher_order_lowlevel) => {
|
||||
call_higher_order_lowlevel(self, ret_sym, ret_layout, *higher_order_lowlevel)
|
||||
}
|
||||
|
||||
CallType::Foreign { .. } => todo!("CallType::Foreign"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -864,8 +1039,13 @@ impl<'a> WasmBackend<'a> {
|
|||
);
|
||||
debug_assert!(!ret_zig_packed_struct);
|
||||
|
||||
let iter = self.proc_lookup.iter().enumerate();
|
||||
for (roc_proc_index, (ir_sym, pl, linker_sym_index)) in iter {
|
||||
for (roc_proc_index, lookup) in self.proc_lookup.iter().enumerate() {
|
||||
let ProcLookupData {
|
||||
name: ir_sym,
|
||||
layout: pl,
|
||||
linker_index: linker_sym_index,
|
||||
..
|
||||
} = lookup;
|
||||
if *ir_sym == func_sym && pl == proc_layout {
|
||||
let wasm_fn_index = self.fn_index_offset + roc_proc_index as u32;
|
||||
self.code_builder.call(
|
||||
|
@ -945,8 +1125,8 @@ impl<'a> WasmBackend<'a> {
|
|||
.call_specialized_equals(ident_ids, arg_layout, arguments);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec);
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
// Generate Wasm code for the IR call expression
|
||||
|
@ -1430,8 +1610,8 @@ impl<'a> WasmBackend<'a> {
|
|||
.call_reset_refcount(ident_ids, layout, argument);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for spec in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec);
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
// Generate Wasm code for the IR call expression
|
||||
|
@ -1442,4 +1622,30 @@ impl<'a> WasmBackend<'a> {
|
|||
ret_storage,
|
||||
);
|
||||
}
|
||||
|
||||
/// Generate a refcount increment procedure and return its Wasm function index
|
||||
pub fn gen_refcount_inc_for_zig(&mut self, layout: Layout<'a>) -> u32 {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
let (proc_symbol, new_specializations) = self
|
||||
.helper_proc_gen
|
||||
.gen_refcount_inc_proc(ident_ids, layout);
|
||||
|
||||
// If any new specializations were created, register their symbol data
|
||||
for (spec_sym, spec_layout) in new_specializations.into_iter() {
|
||||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
let proc_index = self
|
||||
.proc_lookup
|
||||
.iter()
|
||||
.position(|lookup| lookup.name == proc_symbol && lookup.layout.arguments[0] == layout)
|
||||
.unwrap();
|
||||
|
||||
self.fn_index_offset + proc_index as u32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{PTR_SIZE, PTR_TYPE, TARGET_INFO};
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ReturnMethod {
|
||||
/// This layout is returned from a Wasm function "normally" as a Primitive
|
||||
Primitive(ValueType),
|
||||
Primitive(ValueType, u32),
|
||||
/// This layout is returned by writing to a pointer passed as the first argument
|
||||
WriteToPointerArg,
|
||||
/// This layout is empty and requires no return value or argument (e.g. refcount helpers)
|
||||
|
@ -46,8 +46,7 @@ impl WasmLayout {
|
|||
use UnionLayout::*;
|
||||
use ValueType::*;
|
||||
|
||||
let size = layout.stack_size(TARGET_INFO);
|
||||
let alignment_bytes = layout.alignment_bytes(TARGET_INFO);
|
||||
let (size, alignment_bytes) = layout.stack_size_and_alignment(TARGET_INFO);
|
||||
|
||||
match layout {
|
||||
Layout::Builtin(Int(int_width)) => {
|
||||
|
@ -86,9 +85,10 @@ impl WasmLayout {
|
|||
format: StackMemoryFormat::Decimal,
|
||||
},
|
||||
|
||||
Layout::LambdaSet(lambda_set) => WasmLayout::new(&lambda_set.runtime_representation()),
|
||||
|
||||
Layout::Builtin(Str | Dict(_, _) | Set(_) | List(_))
|
||||
| Layout::Struct { .. }
|
||||
| Layout::LambdaSet(_)
|
||||
| Layout::Union(NonRecursive(_)) => Self::StackMemory {
|
||||
size,
|
||||
alignment_bytes,
|
||||
|
@ -125,7 +125,7 @@ impl WasmLayout {
|
|||
|
||||
pub fn return_method(&self, conv: CallConv) -> ReturnMethod {
|
||||
match self {
|
||||
Self::Primitive(ty, _) => ReturnMethod::Primitive(*ty),
|
||||
Self::Primitive(ty, size) => ReturnMethod::Primitive(*ty, *size),
|
||||
Self::StackMemory { size, format, .. } => {
|
||||
conv.stack_memory_return_method(*size, *format)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use roc_mono::ir::{Proc, ProcLayout};
|
|||
use roc_mono::layout::LayoutIds;
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
use crate::backend::WasmBackend;
|
||||
use crate::backend::{ProcLookupData, ProcSource, WasmBackend};
|
||||
use crate::wasm_module::{
|
||||
Align, CodeBuilder, Export, ExportType, LocalId, SymInfo, ValueType, WasmModule,
|
||||
};
|
||||
|
@ -107,7 +107,12 @@ pub fn build_module_without_wrapper<'a>(
|
|||
let linker_sym_index = linker_symbols.len() as u32;
|
||||
|
||||
// linker_sym_index is redundant for these procs from user code, but needed for generated helpers!
|
||||
proc_lookup.push((sym, proc_layout, linker_sym_index));
|
||||
proc_lookup.push(ProcLookupData {
|
||||
name: sym,
|
||||
layout: proc_layout,
|
||||
linker_index: linker_sym_index,
|
||||
source: ProcSource::Roc,
|
||||
});
|
||||
linker_symbols.push(linker_sym);
|
||||
|
||||
fn_index += 1;
|
||||
|
@ -134,7 +139,7 @@ pub fn build_module_without_wrapper<'a>(
|
|||
println!("## procs");
|
||||
for proc in procs.iter() {
|
||||
println!("{}", proc.to_pretty(200));
|
||||
// println!("{:#?}", proc);
|
||||
// println!("{:?}", proc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +149,7 @@ pub fn build_module_without_wrapper<'a>(
|
|||
}
|
||||
|
||||
// Generate specialized helpers for refcounting & equality
|
||||
let helper_procs = backend.generate_helpers();
|
||||
let helper_procs = backend.get_helpers();
|
||||
|
||||
backend.register_symbol_debug_names();
|
||||
|
||||
|
@ -156,9 +161,22 @@ pub fn build_module_without_wrapper<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
// Generate Wasm for refcounting procs
|
||||
for proc in helper_procs.iter() {
|
||||
backend.build_proc(proc);
|
||||
// Generate Wasm for helpers and Zig/Roc wrappers
|
||||
let sources = Vec::from_iter_in(
|
||||
backend
|
||||
.proc_lookup
|
||||
.iter()
|
||||
.map(|ProcLookupData { source, .. }| *source),
|
||||
env.arena,
|
||||
);
|
||||
let mut helper_iter = helper_procs.iter();
|
||||
for (idx, source) in sources.iter().enumerate() {
|
||||
use ProcSource::*;
|
||||
match source {
|
||||
Roc => { /* already generated */ }
|
||||
Helper => backend.build_proc(helper_iter.next().unwrap()),
|
||||
HigherOrderWrapper(inner_idx) => backend.build_higher_order_wrapper(idx, *inner_idx),
|
||||
}
|
||||
}
|
||||
|
||||
let (module, called_preload_fns) = backend.finalize();
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
use bumpalo::collections::Vec;
|
||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::low_level::{LowLevel, LowLevel::*};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::{HigherOrderLowLevel, PassedFunction, ProcLayout};
|
||||
use roc_mono::layout::{Builtin, Layout, UnionLayout};
|
||||
use roc_mono::low_level::HigherOrder;
|
||||
|
||||
use crate::backend::WasmBackend;
|
||||
use crate::layout::CallConv;
|
||||
use crate::layout::{StackMemoryFormat, WasmLayout};
|
||||
use crate::backend::{ProcLookupData, ProcSource, WasmBackend};
|
||||
use crate::layout::{CallConv, StackMemoryFormat, WasmLayout};
|
||||
use crate::storage::{StackMemoryLocation, StoredValue};
|
||||
use crate::wasm_module::{Align, ValueType};
|
||||
use crate::TARGET_INFO;
|
||||
|
||||
/// Number types used for Wasm code gen
|
||||
/// Unlike other enums, this contains no details about layout or storage.
|
||||
|
@ -195,6 +198,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
/// Main entrypoint from WasmBackend
|
||||
pub fn generate(&self, backend: &mut WasmBackend<'a>) {
|
||||
use CodeGenNumType::*;
|
||||
use LowLevel::*;
|
||||
|
||||
let panic_ret_type = || {
|
||||
internal_error!(
|
||||
|
@ -286,14 +290,21 @@ impl<'a> LowLevelCall<'a> {
|
|||
_ => internal_error!("invalid storage for List"),
|
||||
},
|
||||
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||
| ListAny | ListAll | ListFindUnsafe | DictWalk => {
|
||||
internal_error!("HigherOrder lowlevels should not be handled here")
|
||||
}
|
||||
|
||||
ListGetUnsafe | ListReplaceUnsafe | ListSingle | ListRepeat | ListReverse
|
||||
| ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange
|
||||
| ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf
|
||||
| ListWalk | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs
|
||||
| ListSortWith | ListSublist | ListDropAt | ListSwap | ListAny | ListAll
|
||||
| ListFindUnsafe | DictSize | DictEmpty | DictInsert | DictRemove | DictContains
|
||||
| DictGetUnsafe | DictKeys | DictValues | DictUnion | DictIntersection
|
||||
| DictDifference | DictWalk | SetFromList => {
|
||||
| ListSublist | ListDropAt | ListSwap => {
|
||||
todo!("{:?}", self.lowlevel);
|
||||
}
|
||||
|
||||
DictSize | DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe
|
||||
| DictKeys | DictValues | DictUnion | DictIntersection | DictDifference
|
||||
| SetFromList => {
|
||||
todo!("{:?}", self.lowlevel);
|
||||
}
|
||||
|
||||
|
@ -687,9 +698,15 @@ impl<'a> LowLevelCall<'a> {
|
|||
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
|
||||
}
|
||||
}
|
||||
NumToFloatCast => {
|
||||
todo!("implement toF32 and toF64");
|
||||
}
|
||||
NumToIntChecked => {
|
||||
todo!()
|
||||
}
|
||||
NumToFloatChecked => {
|
||||
todo!("implement toF32Checked and toF64Checked");
|
||||
}
|
||||
And => {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.i32_and();
|
||||
|
@ -731,7 +748,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
"Cannot do `==` comparison on different types"
|
||||
);
|
||||
|
||||
let invert_result = matches!(self.lowlevel, NotEq);
|
||||
let invert_result = matches!(self.lowlevel, LowLevel::NotEq);
|
||||
|
||||
match arg_layout {
|
||||
Layout::Builtin(
|
||||
|
@ -942,3 +959,144 @@ fn num_is_finite(backend: &mut WasmBackend<'_>, argument: Symbol) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_higher_order_lowlevel<'a>(
|
||||
backend: &mut WasmBackend<'a>,
|
||||
return_sym: Symbol,
|
||||
return_layout: &Layout<'a>,
|
||||
higher_order: &'a HigherOrderLowLevel<'a>,
|
||||
) {
|
||||
use HigherOrder::*;
|
||||
|
||||
let HigherOrderLowLevel {
|
||||
op,
|
||||
passed_function,
|
||||
..
|
||||
} = higher_order;
|
||||
|
||||
let PassedFunction {
|
||||
name: fn_name,
|
||||
argument_layouts,
|
||||
return_layout: result_layout,
|
||||
owns_captured_environment,
|
||||
captured_environment,
|
||||
..
|
||||
} = passed_function;
|
||||
|
||||
let closure_data_layout = match backend.storage.symbol_layouts[captured_environment] {
|
||||
Layout::LambdaSet(lambda_set) => lambda_set.runtime_representation(),
|
||||
Layout::Struct {
|
||||
field_layouts: &[], ..
|
||||
} => Layout::UNIT,
|
||||
x => internal_error!("Closure data has an invalid layout\n{:?}", x),
|
||||
};
|
||||
let closure_data_exists: bool = closure_data_layout != Layout::UNIT;
|
||||
|
||||
// We create a wrapper around the passed function, which just unboxes the arguments.
|
||||
// This allows Zig builtins to have a generic pointer-based interface.
|
||||
let source = {
|
||||
let passed_proc_layout = ProcLayout {
|
||||
arguments: argument_layouts,
|
||||
result: *result_layout,
|
||||
};
|
||||
let passed_proc_index = backend
|
||||
.proc_lookup
|
||||
.iter()
|
||||
.position(|ProcLookupData { name, layout, .. }| {
|
||||
name == fn_name && layout == &passed_proc_layout
|
||||
})
|
||||
.unwrap();
|
||||
ProcSource::HigherOrderWrapper(passed_proc_index)
|
||||
};
|
||||
let wrapper_sym = backend.create_symbol(&format!("#wrap#{:?}", fn_name));
|
||||
let wrapper_layout = {
|
||||
let mut wrapper_arg_layouts: Vec<Layout<'a>> =
|
||||
Vec::with_capacity_in(argument_layouts.len() + 1, backend.env.arena);
|
||||
|
||||
let n_non_closure_args = if closure_data_exists {
|
||||
argument_layouts.len() - 1
|
||||
} else {
|
||||
argument_layouts.len()
|
||||
};
|
||||
|
||||
wrapper_arg_layouts.push(closure_data_layout);
|
||||
wrapper_arg_layouts.extend(
|
||||
argument_layouts
|
||||
.iter()
|
||||
.take(n_non_closure_args)
|
||||
.map(Layout::Boxed),
|
||||
);
|
||||
wrapper_arg_layouts.push(Layout::Boxed(result_layout));
|
||||
|
||||
ProcLayout {
|
||||
arguments: wrapper_arg_layouts.into_bump_slice(),
|
||||
result: Layout::UNIT,
|
||||
}
|
||||
};
|
||||
|
||||
let wrapper_fn_idx = backend.register_helper_proc(wrapper_sym, wrapper_layout, source);
|
||||
let inc_fn_idx = backend.gen_refcount_inc_for_zig(closure_data_layout);
|
||||
|
||||
let wrapper_fn_ptr = backend.get_fn_table_index(wrapper_fn_idx);
|
||||
let inc_fn_ptr = backend.get_fn_table_index(inc_fn_idx);
|
||||
|
||||
match op {
|
||||
// List.map : List elem_x, (elem_x -> elem_ret) -> List elem_ret
|
||||
ListMap { xs } => {
|
||||
let list_layout_in = backend.storage.symbol_layouts[xs];
|
||||
|
||||
let (elem_x, elem_ret) = match (list_layout_in, return_layout) {
|
||||
(
|
||||
Layout::Builtin(Builtin::List(elem_x)),
|
||||
Layout::Builtin(Builtin::List(elem_ret)),
|
||||
) => (elem_x, elem_ret),
|
||||
_ => unreachable!("invalid layout for List.map arguments"),
|
||||
};
|
||||
let elem_x_size = elem_x.stack_size(TARGET_INFO);
|
||||
let (elem_ret_size, elem_ret_align) = elem_ret.stack_size_and_alignment(TARGET_INFO);
|
||||
|
||||
let cb = &mut backend.code_builder;
|
||||
|
||||
// Load return pointer & argument values
|
||||
// Wasm signature: (i32, i64, i64, i32, i32, i32, i32, i32, i32, i32) -> nil
|
||||
backend.storage.load_symbols(cb, &[return_sym]);
|
||||
backend.storage.load_symbol_zig(cb, *xs); // list with capacity = 2 x i64 args
|
||||
cb.i32_const(wrapper_fn_ptr);
|
||||
if closure_data_exists {
|
||||
backend.storage.load_symbols(cb, &[*captured_environment]);
|
||||
} else {
|
||||
// Normally, a zero-size arg would be eliminated in code gen, but Zig expects one!
|
||||
cb.i32_const(0); // null pointer
|
||||
}
|
||||
cb.i32_const(inc_fn_ptr);
|
||||
cb.i32_const(*owns_captured_environment as i32);
|
||||
cb.i32_const(elem_ret_align as i32); // used for allocating the new list
|
||||
cb.i32_const(elem_x_size as i32);
|
||||
cb.i32_const(elem_ret_size as i32);
|
||||
|
||||
let num_wasm_args = 10; // 1 return pointer + 8 Zig args + list 2nd i64
|
||||
let has_return_val = false;
|
||||
backend.call_zig_builtin_after_loading_args(
|
||||
bitcode::LIST_MAP,
|
||||
num_wasm_args,
|
||||
has_return_val,
|
||||
);
|
||||
}
|
||||
|
||||
ListMap2 { .. }
|
||||
| ListMap3 { .. }
|
||||
| ListMap4 { .. }
|
||||
| ListMapWithIndex { .. }
|
||||
| ListKeepIf { .. }
|
||||
| ListWalk { .. }
|
||||
| ListWalkUntil { .. }
|
||||
| ListWalkBackwards { .. }
|
||||
| ListKeepOks { .. }
|
||||
| ListKeepErrs { .. }
|
||||
| ListSortWith { .. }
|
||||
| ListAny { .. }
|
||||
| ListAll { .. }
|
||||
| ListFindUnsafe { .. }
|
||||
| DictWalk { .. } => todo!("{:?}", op),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -299,7 +299,8 @@ impl<'a> Storage<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_symbol_zig(&mut self, code_builder: &mut CodeBuilder, arg: Symbol) {
|
||||
// TODO: expose something higher level instead, shared among higher-order calls
|
||||
pub fn load_symbol_zig(&mut self, code_builder: &mut CodeBuilder, arg: Symbol) {
|
||||
if let StoredValue::StackMemory {
|
||||
location,
|
||||
size,
|
||||
|
@ -402,7 +403,7 @@ impl<'a> Storage<'a> {
|
|||
|
||||
let return_method = return_layout.return_method(call_conv);
|
||||
let has_return_val = match return_method {
|
||||
Primitive(_) => true,
|
||||
Primitive(..) => true,
|
||||
NoReturnValue => false,
|
||||
WriteToPointerArg => {
|
||||
num_wasm_args += 1;
|
||||
|
@ -508,7 +509,7 @@ impl<'a> Storage<'a> {
|
|||
size
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ impl std::fmt::Debug for VmBlock<'_> {
|
|||
/// Rust representation matches Wasm encoding.
|
||||
/// It's an error to specify alignment higher than the "natural" alignment of the instruction
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||
pub enum Align {
|
||||
Bytes1 = 0,
|
||||
Bytes2 = 1,
|
||||
|
@ -78,6 +78,23 @@ pub enum Align {
|
|||
Bytes8 = 3,
|
||||
}
|
||||
|
||||
impl Align {
|
||||
/// Calculate the largest possible alignment for a load/store at a given stack frame offset
|
||||
/// Assumes the stack frame is aligned to at least 8 bytes
|
||||
pub fn from_stack_offset(max_align: Align, offset: u32) -> Align {
|
||||
if (max_align == Align::Bytes8) && (offset & 7 == 0) {
|
||||
return Align::Bytes8;
|
||||
}
|
||||
if (max_align >= Align::Bytes4) && (offset & 3 == 0) {
|
||||
return Align::Bytes4;
|
||||
}
|
||||
if (max_align >= Align::Bytes2) && (offset & 1 == 0) {
|
||||
return Align::Bytes2;
|
||||
}
|
||||
Align::Bytes1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Align {
|
||||
fn from(x: u32) -> Align {
|
||||
match x {
|
||||
|
|
|
@ -14,7 +14,8 @@ pub use sections::{ConstExpr, Export, ExportType, Global, GlobalType, Signature}
|
|||
use self::linking::{LinkingSection, RelocationSection};
|
||||
use self::sections::{
|
||||
CodeSection, DataSection, ElementSection, ExportSection, FunctionSection, GlobalSection,
|
||||
ImportSection, MemorySection, NameSection, OpaqueSection, Section, SectionId, TypeSection,
|
||||
ImportSection, MemorySection, NameSection, OpaqueSection, Section, SectionId, TableSection,
|
||||
TypeSection,
|
||||
};
|
||||
use self::serialize::{SerialBuffer, Serialize};
|
||||
|
||||
|
@ -25,7 +26,7 @@ pub struct WasmModule<'a> {
|
|||
pub types: TypeSection<'a>,
|
||||
pub import: ImportSection<'a>,
|
||||
pub function: FunctionSection<'a>,
|
||||
pub table: OpaqueSection<'a>,
|
||||
pub table: TableSection,
|
||||
pub memory: MemorySection<'a>,
|
||||
pub global: GlobalSection<'a>,
|
||||
pub export: ExportSection<'a>,
|
||||
|
@ -138,7 +139,7 @@ impl<'a> WasmModule<'a> {
|
|||
let function = FunctionSection::preload(arena, bytes, &mut cursor);
|
||||
let defined_fn_signatures = function.parse(arena);
|
||||
|
||||
let table = OpaqueSection::preload(SectionId::Table, arena, bytes, &mut cursor);
|
||||
let table = TableSection::preload(bytes, &mut cursor);
|
||||
|
||||
let memory = MemorySection::preload(arena, bytes, &mut cursor);
|
||||
|
||||
|
|
|
@ -276,33 +276,6 @@ impl<'a> Section<'a> for TypeSection<'a> {
|
|||
*
|
||||
*******************************************************************/
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum RefType {
|
||||
Func = 0x70,
|
||||
Extern = 0x6f,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TableType {
|
||||
pub ref_type: RefType,
|
||||
pub limits: Limits,
|
||||
}
|
||||
|
||||
impl Serialize for TableType {
|
||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||
buffer.append_u8(self.ref_type as u8);
|
||||
self.limits.serialize(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
impl SkipBytes for TableType {
|
||||
fn skip_bytes(bytes: &[u8], cursor: &mut usize) {
|
||||
u8::skip_bytes(bytes, cursor);
|
||||
Limits::skip_bytes(bytes, cursor);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ImportDesc {
|
||||
Func { signature_index: u32 },
|
||||
|
@ -457,6 +430,104 @@ impl<'a> FunctionSection<'a> {
|
|||
|
||||
section_impl!(FunctionSection, SectionId::Function);
|
||||
|
||||
/*******************************************************************
|
||||
*
|
||||
* Table section
|
||||
*
|
||||
* Defines tables used for indirect references to host memory.
|
||||
* The table *contents* are elsewhere, in the ElementSection.
|
||||
*
|
||||
*******************************************************************/
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum RefType {
|
||||
Func = 0x70,
|
||||
Extern = 0x6f,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TableType {
|
||||
pub ref_type: RefType,
|
||||
pub limits: Limits,
|
||||
}
|
||||
|
||||
impl Serialize for TableType {
|
||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||
buffer.append_u8(self.ref_type as u8);
|
||||
self.limits.serialize(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
impl SkipBytes for TableType {
|
||||
fn skip_bytes(bytes: &[u8], cursor: &mut usize) {
|
||||
u8::skip_bytes(bytes, cursor);
|
||||
Limits::skip_bytes(bytes, cursor);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TableSection {
|
||||
pub function_table: TableType,
|
||||
}
|
||||
|
||||
impl TableSection {
|
||||
const ID: SectionId = SectionId::Table;
|
||||
|
||||
pub fn preload(module_bytes: &[u8], mod_cursor: &mut usize) -> Self {
|
||||
let (count, section_bytes) = parse_section(Self::ID, module_bytes, mod_cursor);
|
||||
|
||||
match count {
|
||||
0 => TableSection {
|
||||
function_table: TableType {
|
||||
ref_type: RefType::Func,
|
||||
limits: Limits::MinMax(0, 0),
|
||||
},
|
||||
},
|
||||
1 => {
|
||||
if section_bytes[0] != RefType::Func as u8 {
|
||||
internal_error!("Only funcref tables are supported")
|
||||
}
|
||||
let mut section_cursor = 1;
|
||||
let limits = Limits::parse(section_bytes, &mut section_cursor);
|
||||
|
||||
TableSection {
|
||||
function_table: TableType {
|
||||
ref_type: RefType::Func,
|
||||
limits,
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => internal_error!("Multiple tables are not supported"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
let section_id_bytes = 1;
|
||||
let section_length_bytes = 1;
|
||||
let num_tables_bytes = 1;
|
||||
let ref_type_bytes = 1;
|
||||
let limits_bytes = match self.function_table.limits {
|
||||
Limits::Min(_) => MAX_SIZE_ENCODED_U32,
|
||||
Limits::MinMax(..) => 2 * MAX_SIZE_ENCODED_U32,
|
||||
};
|
||||
|
||||
section_id_bytes + section_length_bytes + num_tables_bytes + ref_type_bytes + limits_bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for TableSection {
|
||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||
let header_indices = write_section_header(buffer, Self::ID);
|
||||
|
||||
let num_tables: u32 = 1;
|
||||
num_tables.serialize(buffer);
|
||||
self.function_table.serialize(buffer);
|
||||
|
||||
update_section_size(buffer, header_indices);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*
|
||||
* Memory section
|
||||
|
@ -502,6 +573,21 @@ impl SkipBytes for Limits {
|
|||
}
|
||||
}
|
||||
|
||||
impl Limits {
|
||||
fn parse(bytes: &[u8], cursor: &mut usize) -> Self {
|
||||
let variant_id = bytes[*cursor];
|
||||
*cursor += 1;
|
||||
|
||||
let min = parse_u32_or_panic(bytes, cursor);
|
||||
if variant_id == LimitsId::MinMax as u8 {
|
||||
let max = parse_u32_or_panic(bytes, cursor);
|
||||
Limits::MinMax(min, max)
|
||||
} else {
|
||||
Limits::Min(min)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemorySection<'a> {
|
||||
pub count: u32,
|
||||
|
@ -584,6 +670,13 @@ impl ConstExpr {
|
|||
|
||||
value
|
||||
}
|
||||
|
||||
fn unwrap_i32(&self) -> i32 {
|
||||
match self {
|
||||
Self::I32(x) => *x,
|
||||
_ => internal_error!("Expected ConstExpr to be I32"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ConstExpr {
|
||||
|
@ -781,9 +874,10 @@ enum ElementSegmentFormatId {
|
|||
ActiveImplicitTableIndex = 0x00,
|
||||
}
|
||||
|
||||
/// A Segment initialises a subrange of elements in a table. Normally there's just one Segment.
|
||||
#[derive(Debug)]
|
||||
struct ElementSegment<'a> {
|
||||
offset: ConstExpr,
|
||||
offset: ConstExpr, // The starting table index for the segment
|
||||
fn_indices: Vec<'a, u32>,
|
||||
}
|
||||
|
||||
|
@ -834,6 +928,8 @@ impl<'a> Serialize for ElementSegment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// An Element is an entry in a Table (see TableSection)
|
||||
/// The only currently supported Element type is a function reference, used for indirect calls.
|
||||
#[derive(Debug)]
|
||||
pub struct ElementSection<'a> {
|
||||
segments: Vec<'a, ElementSegment<'a>>,
|
||||
|
@ -845,6 +941,15 @@ impl<'a> ElementSection<'a> {
|
|||
pub fn preload(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self {
|
||||
let (num_segments, body_bytes) = parse_section(Self::ID, module_bytes, cursor);
|
||||
|
||||
if num_segments == 0 {
|
||||
let seg = ElementSegment {
|
||||
offset: ConstExpr::I32(1),
|
||||
fn_indices: bumpalo::vec![in arena],
|
||||
};
|
||||
ElementSection {
|
||||
segments: bumpalo::vec![in arena; seg],
|
||||
}
|
||||
} else {
|
||||
let mut segments = Vec::with_capacity_in(num_segments as usize, arena);
|
||||
|
||||
let mut body_cursor = 0;
|
||||
|
@ -852,10 +957,41 @@ impl<'a> ElementSection<'a> {
|
|||
let seg = ElementSegment::parse(arena, body_bytes, &mut body_cursor);
|
||||
segments.push(seg);
|
||||
}
|
||||
|
||||
ElementSection { segments }
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a table index for a function (equivalent to a function pointer)
|
||||
/// The function will be inserted into the table if it's not already there.
|
||||
/// This index is what the call_indirect instruction expects.
|
||||
/// (This works mostly the same as function pointers, except hackers can't jump to arbitrary code)
|
||||
pub fn get_fn_table_index(&mut self, fn_index: u32) -> i32 {
|
||||
// In practice there is always one segment. We allow a bit more generality by using the last one.
|
||||
let segment = self.segments.last_mut().unwrap();
|
||||
let offset = segment.offset.unwrap_i32();
|
||||
let pos = segment.fn_indices.iter().position(|f| *f == fn_index);
|
||||
if let Some(existing_table_index) = pos {
|
||||
offset + existing_table_index as i32
|
||||
} else {
|
||||
let new_table_index = segment.fn_indices.len();
|
||||
segment.fn_indices.push(fn_index);
|
||||
offset + new_table_index as i32
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of elements in the table
|
||||
pub fn max_table_index(&self) -> u32 {
|
||||
let mut result = 0;
|
||||
for s in self.segments.iter() {
|
||||
let max_index = s.offset.unwrap_i32() + s.fn_indices.len() as i32;
|
||||
if max_index > result {
|
||||
result = max_index;
|
||||
}
|
||||
}
|
||||
result as u32
|
||||
}
|
||||
|
||||
/// Approximate serialized byte size (for buffer capacity)
|
||||
pub fn size(&self) -> usize {
|
||||
self.segments.iter().map(|seg| seg.size()).sum()
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ use roc_can::scope::Scope;
|
|||
use roc_error_macros::todo_abilities;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::symbol::IdentIds;
|
||||
use roc_parse::ast::CommentOrNewline;
|
||||
use roc_parse::ast::{self, TypeHeader};
|
||||
use roc_parse::ast::{AssignedField, Def};
|
||||
use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef};
|
||||
use roc_region::all::Loc;
|
||||
|
||||
// Documentation generation requirements
|
||||
|
@ -166,7 +166,8 @@ fn generate_entry_doc<'a>(
|
|||
(new_acc, Some(comments_or_new_lines))
|
||||
}
|
||||
|
||||
Def::Annotation(loc_pattern, loc_ann) => match loc_pattern.value {
|
||||
Def::Value(def) => match def {
|
||||
ValueDef::Annotation(loc_pattern, loc_ann) => match loc_pattern.value {
|
||||
Pattern::Identifier(identifier) => {
|
||||
// Check if the definition is exposed
|
||||
if ident_ids.get_id(&identifier.into()).is_some() {
|
||||
|
@ -175,7 +176,8 @@ fn generate_entry_doc<'a>(
|
|||
name,
|
||||
type_annotation: type_to_docs(false, loc_ann.value),
|
||||
type_vars: Vec::new(),
|
||||
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
|
||||
docs: before_comments_or_new_lines
|
||||
.and_then(comments_or_new_lines_to_docs),
|
||||
};
|
||||
acc.push(DocEntry::DocDef(doc_def));
|
||||
}
|
||||
|
@ -184,7 +186,8 @@ fn generate_entry_doc<'a>(
|
|||
|
||||
_ => (acc, None),
|
||||
},
|
||||
Def::AnnotatedBody {
|
||||
|
||||
ValueDef::AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
..
|
||||
|
@ -196,7 +199,8 @@ fn generate_entry_doc<'a>(
|
|||
name: identifier.to_string(),
|
||||
type_annotation: type_to_docs(false, ann_type.value),
|
||||
type_vars: Vec::new(),
|
||||
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
|
||||
docs: before_comments_or_new_lines
|
||||
.and_then(comments_or_new_lines_to_docs),
|
||||
};
|
||||
acc.push(DocEntry::DocDef(doc_def));
|
||||
}
|
||||
|
@ -206,7 +210,13 @@ fn generate_entry_doc<'a>(
|
|||
_ => (acc, None),
|
||||
},
|
||||
|
||||
Def::Alias {
|
||||
ValueDef::Body(_, _) => (acc, None),
|
||||
|
||||
ValueDef::Expect(c) => todo!("documentation for tests {:?}", c),
|
||||
},
|
||||
|
||||
Def::Type(def) => match def {
|
||||
TypeDef::Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
} => {
|
||||
|
@ -229,7 +239,7 @@ fn generate_entry_doc<'a>(
|
|||
(acc, None)
|
||||
}
|
||||
|
||||
Def::Opaque {
|
||||
TypeDef::Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
..
|
||||
} => {
|
||||
|
@ -252,11 +262,8 @@ fn generate_entry_doc<'a>(
|
|||
(acc, None)
|
||||
}
|
||||
|
||||
Def::Ability { .. } => todo_abilities!(),
|
||||
|
||||
Def::Body(_, _) => (acc, None),
|
||||
|
||||
Def::Expect(c) => todo!("documentation for tests {:?}", c),
|
||||
TypeDef::Ability { .. } => todo_abilities!(),
|
||||
},
|
||||
|
||||
Def::NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
|
|
|
@ -111,7 +111,9 @@ pub enum LowLevel {
|
|||
NumShiftRightBy,
|
||||
NumShiftRightZfBy,
|
||||
NumIntCast,
|
||||
NumToFloatCast,
|
||||
NumToIntChecked,
|
||||
NumToFloatChecked,
|
||||
NumToStr,
|
||||
Eq,
|
||||
NotEq,
|
||||
|
@ -240,7 +242,7 @@ impl LowLevelWrapperType {
|
|||
Symbol::LIST_PREPEND => CanBeReplacedBy(ListPrepend),
|
||||
Symbol::LIST_JOIN => CanBeReplacedBy(ListJoin),
|
||||
Symbol::LIST_RANGE => CanBeReplacedBy(ListRange),
|
||||
Symbol::LIST_MAP => CanBeReplacedBy(ListMap),
|
||||
Symbol::LIST_MAP => WrapperIsRequired,
|
||||
Symbol::LIST_MAP2 => CanBeReplacedBy(ListMap2),
|
||||
Symbol::LIST_MAP3 => CanBeReplacedBy(ListMap3),
|
||||
Symbol::LIST_MAP4 => CanBeReplacedBy(ListMap4),
|
||||
|
|
|
@ -1053,6 +1053,10 @@ define_builtins! {
|
|||
144 NUM_TO_U128_CHECKED: "toU128Checked"
|
||||
145 NUM_TO_NAT: "toNat"
|
||||
146 NUM_TO_NAT_CHECKED: "toNatChecked"
|
||||
147 NUM_TO_F32: "toF32"
|
||||
148 NUM_TO_F32_CHECKED: "toF32Checked"
|
||||
149 NUM_TO_F64: "toF64"
|
||||
150 NUM_TO_F64_CHECKED: "toF64Checked"
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
|
@ -1103,7 +1107,6 @@ define_builtins! {
|
|||
32 STR_TO_I16: "toI16"
|
||||
33 STR_TO_U8: "toU8"
|
||||
34 STR_TO_I8: "toI8"
|
||||
|
||||
}
|
||||
4 LIST: "List" => {
|
||||
0 LIST_LIST: "List" imported // the List.List type alias
|
||||
|
|
|
@ -1001,7 +1001,9 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
|
||||
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
|
||||
| NumRound | NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos
|
||||
| NumAsin | NumIntCast | NumToIntChecked => arena.alloc_slice_copy(&[irrelevant]),
|
||||
| NumAsin | NumIntCast | NumToIntChecked | NumToFloatCast | NumToFloatChecked => {
|
||||
arena.alloc_slice_copy(&[irrelevant])
|
||||
}
|
||||
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||
|
|
|
@ -182,6 +182,26 @@ impl<'a> CodeGenHelp<'a> {
|
|||
(expr, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
/// Generate a refcount increment procedure, *without* a Call expression.
|
||||
/// *This method should be rarely used* - only when the proc is to be called from Zig.
|
||||
/// Otherwise you want to generate the Proc and the Call together, using another method.
|
||||
/// This only supports the 'inc' operation, as it's the only real use case we have.
|
||||
pub fn gen_refcount_inc_proc(
|
||||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
layout: Layout<'a>,
|
||||
) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) {
|
||||
let mut ctx = Context {
|
||||
new_linker_data: Vec::new_in(self.arena),
|
||||
recursive_union: None,
|
||||
op: HelperOp::Inc,
|
||||
};
|
||||
|
||||
let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout);
|
||||
|
||||
(proc_name, ctx.new_linker_data)
|
||||
}
|
||||
|
||||
/// Replace a generic `Lowlevel::Eq` call with a specialized helper proc.
|
||||
/// The helper procs themselves are to be generated later with `generate_procs`
|
||||
pub fn call_specialized_equals(
|
||||
|
@ -512,24 +532,18 @@ fn layout_needs_helper_proc(layout: &Layout, op: HelperOp) -> bool {
|
|||
Layout::Builtin(Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal) => {
|
||||
false
|
||||
}
|
||||
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
// Str type can use either Zig functions or generated IR, since it's not generic.
|
||||
// Eq uses a Zig function, refcount uses generated IR.
|
||||
// Both are fine, they were just developed at different times.
|
||||
matches!(op, HelperOp::Inc | HelperOp::Dec | HelperOp::DecRef(_))
|
||||
}
|
||||
|
||||
Layout::Builtin(Builtin::Dict(_, _) | Builtin::Set(_) | Builtin::List(_)) => true,
|
||||
|
||||
Layout::Struct { field_layouts, .. } => !field_layouts.is_empty(),
|
||||
|
||||
Layout::Struct { .. } => true, // note: we do generate a helper for Unit, with just a Stmt::Ret
|
||||
Layout::Union(UnionLayout::NonRecursive(tags)) => !tags.is_empty(),
|
||||
|
||||
Layout::Union(_) => true,
|
||||
|
||||
Layout::LambdaSet(_) | Layout::RecursivePointer => false,
|
||||
|
||||
Layout::LambdaSet(_) => true,
|
||||
Layout::RecursivePointer => false,
|
||||
Layout::Boxed(_) => true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use roc_exhaustive::{Ctor, Guard, RenderAs, TagId};
|
|||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
use roc_problem::can::RuntimeError;
|
||||
use roc_problem::can::{RuntimeError, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_std::RocDec;
|
||||
use roc_target::TargetInfo;
|
||||
|
@ -2037,6 +2037,7 @@ fn pattern_to_when<'a>(
|
|||
let error = roc_problem::can::RuntimeError::Shadowing {
|
||||
original_region: *region,
|
||||
shadow: loc_ident.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
};
|
||||
(*new_symbol, Loc::at_zero(RuntimeError(error)))
|
||||
}
|
||||
|
@ -2088,6 +2089,13 @@ fn pattern_to_when<'a>(
|
|||
// They should have been replaced with `UnsupportedPattern` during canonicalization
|
||||
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
|
||||
}
|
||||
|
||||
AbilityMemberSpecialization { .. } => {
|
||||
unreachable!(
|
||||
"Ability member specialization {:?} should never appear in a when!",
|
||||
pattern.value
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3053,7 +3061,7 @@ fn specialize_naked_symbol<'a>(
|
|||
let opt_fn_var = Some(variable);
|
||||
|
||||
// if this is a function symbol, ensure that it's properly specialized!
|
||||
reuse_function_symbol(
|
||||
specialize_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
|
@ -3432,7 +3440,6 @@ pub fn with_hole<'a>(
|
|||
ZeroArgumentTag {
|
||||
variant_var,
|
||||
name: tag_name,
|
||||
arguments: args,
|
||||
ext_var,
|
||||
closure_name,
|
||||
} => {
|
||||
|
@ -3466,7 +3473,7 @@ pub fn with_hole<'a>(
|
|||
tag_name,
|
||||
procs,
|
||||
layout_cache,
|
||||
args,
|
||||
std::vec::Vec::new(),
|
||||
arena,
|
||||
)
|
||||
}
|
||||
|
@ -3558,7 +3565,7 @@ pub fn with_hole<'a>(
|
|||
// this symbol is already defined; nothing to do
|
||||
}
|
||||
Field::Function(symbol, variable) => {
|
||||
stmt = reuse_function_symbol(
|
||||
stmt = specialize_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
|
@ -4114,7 +4121,7 @@ pub fn with_hole<'a>(
|
|||
Stmt::Let(*symbol, access_expr, *field_layout, arena.alloc(stmt));
|
||||
|
||||
if record_needs_specialization {
|
||||
stmt = reuse_function_symbol(
|
||||
stmt = specialize_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
|
@ -4804,8 +4811,7 @@ fn construct_closure_data<'a>(
|
|||
// symbols to be inlined when specializing the closure body elsewhere.
|
||||
for &&(symbol, var) in symbols {
|
||||
if procs.partial_exprs.contains(symbol) {
|
||||
result =
|
||||
reuse_function_symbol(env, procs, layout_cache, Some(var), symbol, result, symbol);
|
||||
result = specialize_symbol(env, procs, layout_cache, Some(var), symbol, result, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6318,6 +6324,20 @@ fn store_pattern_help<'a>(
|
|||
|
||||
match can_pat {
|
||||
Identifier(symbol) => {
|
||||
if let Some((_, var)) = procs.partial_exprs.get(outer_symbol) {
|
||||
// It might be the case that symbol we're storing hasn't been reified to a value
|
||||
// yet, if it's polymorphic. Do that now.
|
||||
stmt = specialize_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(var),
|
||||
*symbol,
|
||||
stmt,
|
||||
outer_symbol,
|
||||
);
|
||||
}
|
||||
|
||||
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
||||
}
|
||||
Underscore => {
|
||||
|
@ -6769,9 +6789,8 @@ fn let_empty_struct<'a>(assigned: Symbol, hole: &'a Stmt<'a>) -> Stmt<'a> {
|
|||
Stmt::Let(assigned, Expr::Struct(&[]), Layout::UNIT, hole)
|
||||
}
|
||||
|
||||
/// If the symbol is a function, make sure it is properly specialized
|
||||
// TODO: rename this now that we handle polymorphic non-function expressions too
|
||||
fn reuse_function_symbol<'a>(
|
||||
/// If the symbol is a function or polymorphic value, make sure it is properly specialized
|
||||
fn specialize_symbol<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
|
@ -6980,7 +6999,7 @@ fn assign_to_symbol<'a>(
|
|||
match can_reuse_symbol(env, procs, &loc_arg.value) {
|
||||
Imported(original) | LocalFunction(original) | UnspecializedExpr(original) => {
|
||||
// for functions we must make sure they are specialized correctly
|
||||
reuse_function_symbol(
|
||||
specialize_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
|
@ -7787,6 +7806,7 @@ fn from_can_pattern_help<'a>(
|
|||
match can_pattern {
|
||||
Underscore => Ok(Pattern::Underscore),
|
||||
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
||||
AbilityMemberSpecialization { ident, .. } => Ok(Pattern::Identifier(*ident)),
|
||||
IntLiteral(_, precision_var, _, int, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision_var, false) {
|
||||
IntOrFloat::Int(precision) => {
|
||||
|
@ -7830,6 +7850,7 @@ fn from_can_pattern_help<'a>(
|
|||
Shadowed(region, ident, _new_symbol) => Err(RuntimeError::Shadowing {
|
||||
original_region: *region,
|
||||
shadow: ident.clone(),
|
||||
kind: ShadowKind::Variable,
|
||||
}),
|
||||
UnsupportedPattern(region) => Err(RuntimeError::UnsupportedPattern(*region)),
|
||||
MalformedPattern(_problem, region) => {
|
||||
|
|
|
@ -468,7 +468,7 @@ impl<'a> UnionLayout<'a> {
|
|||
|
||||
pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
||||
let allocation = match self {
|
||||
UnionLayout::NonRecursive(_) => unreachable!("not heap-allocated"),
|
||||
UnionLayout::NonRecursive(tags) => Self::tags_alignment_bytes(tags, target_info),
|
||||
UnionLayout::Recursive(tags) => Self::tags_alignment_bytes(tags, target_info),
|
||||
UnionLayout::NonNullableUnwrapped(field_layouts) => {
|
||||
Layout::struct_no_name_order(field_layouts).alignment_bytes(target_info)
|
||||
|
@ -1150,9 +1150,11 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
|
||||
pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
||||
let ptr_width = target_info.ptr_width() as u32;
|
||||
|
||||
match self {
|
||||
Layout::Builtin(builtin) => builtin.allocation_alignment_bytes(target_info),
|
||||
Layout::Struct { .. } => unreachable!("not heap-allocated"),
|
||||
Layout::Struct { .. } => self.alignment_bytes(target_info).max(ptr_width),
|
||||
Layout::Union(union_layout) => union_layout.allocation_alignment_bytes(target_info),
|
||||
Layout::LambdaSet(lambda_set) => lambda_set
|
||||
.runtime_representation()
|
||||
|
@ -1545,9 +1547,6 @@ impl<'a> Builtin<'a> {
|
|||
let ptr_width = target_info.ptr_width() as u32;
|
||||
|
||||
let allocation = match self {
|
||||
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => {
|
||||
unreachable!("not heap-allocated")
|
||||
}
|
||||
Builtin::Str => ptr_width,
|
||||
Builtin::Dict(k, v) => k
|
||||
.alignment_bytes(target_info)
|
||||
|
@ -1555,6 +1554,11 @@ impl<'a> Builtin<'a> {
|
|||
.max(ptr_width),
|
||||
Builtin::Set(k) => k.alignment_bytes(target_info).max(ptr_width),
|
||||
Builtin::List(e) => e.alignment_bytes(target_info).max(ptr_width),
|
||||
// The following are usually not heap-allocated, but they might be when inside a Box.
|
||||
Builtin::Int(int_width) => int_width.alignment_bytes(target_info).max(ptr_width),
|
||||
Builtin::Float(float_width) => float_width.alignment_bytes(target_info).max(ptr_width),
|
||||
Builtin::Bool => (core::mem::align_of::<bool>() as u32).max(ptr_width),
|
||||
Builtin::Decimal => IntWidth::I128.alignment_bytes(target_info).max(ptr_width),
|
||||
};
|
||||
|
||||
allocation.max(ptr_width)
|
||||
|
|
|
@ -170,6 +170,7 @@ enum FirstOrder {
|
|||
NumBytesToU32,
|
||||
NumShiftRightZfBy,
|
||||
NumIntCast,
|
||||
NumFloatCast,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
|
|
@ -273,17 +273,13 @@ pub enum Has<'a> {
|
|||
/// An ability demand is a value defining the ability; for example `hash : a -> U64 | a has Hash`
|
||||
/// for a `Hash` ability.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct AbilityDemand<'a> {
|
||||
pub struct AbilityMember<'a> {
|
||||
pub name: Loc<Spaced<'a, &'a str>>,
|
||||
pub typ: Loc<TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Def<'a> {
|
||||
// TODO in canonicalization, validate the pattern; only certain patterns
|
||||
// are allowed in annotations.
|
||||
Annotation(Loc<Pattern<'a>>, Loc<TypeAnnotation<'a>>),
|
||||
|
||||
pub enum TypeDef<'a> {
|
||||
/// A type alias. This is like a standalone annotation, except the pattern
|
||||
/// must be a capitalized Identifier, e.g.
|
||||
///
|
||||
|
@ -305,8 +301,15 @@ pub enum Def<'a> {
|
|||
Ability {
|
||||
header: TypeHeader<'a>,
|
||||
loc_has: Loc<Has<'a>>,
|
||||
demands: &'a [AbilityDemand<'a>],
|
||||
members: &'a [AbilityMember<'a>],
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ValueDef<'a> {
|
||||
// TODO in canonicalization, validate the pattern; only certain patterns
|
||||
// are allowed in annotations.
|
||||
Annotation(Loc<Pattern<'a>>, Loc<TypeAnnotation<'a>>),
|
||||
|
||||
// TODO in canonicalization, check to see if there are any newlines after the
|
||||
// annotation; if not, and if it's followed by a Body, then the annotation
|
||||
|
@ -323,6 +326,12 @@ pub enum Def<'a> {
|
|||
},
|
||||
|
||||
Expect(&'a Loc<Expr<'a>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Def<'a> {
|
||||
Type(TypeDef<'a>),
|
||||
Value(ValueDef<'a>),
|
||||
|
||||
// Blank Space (e.g. comments, spaces, newlines) before or after a def.
|
||||
// We preserve this for the formatter; canonicalization ignores it.
|
||||
|
@ -341,6 +350,30 @@ impl<'a> Def<'a> {
|
|||
debug_assert!(!matches!(def, Def::SpaceBefore(_, _)));
|
||||
(spaces, def)
|
||||
}
|
||||
|
||||
pub fn unroll_def(&self) -> Result<&TypeDef<'a>, &ValueDef<'a>> {
|
||||
let mut def = self;
|
||||
loop {
|
||||
match def {
|
||||
Def::Type(type_def) => return Ok(type_def),
|
||||
Def::Value(value_def) => return Err(value_def),
|
||||
Def::SpaceBefore(def1, _) | Def::SpaceAfter(def1, _) => def = def1,
|
||||
Def::NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<TypeDef<'a>> for Def<'a> {
|
||||
fn from(def: TypeDef<'a>) -> Self {
|
||||
Self::Type(def)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<ValueDef<'a>> for Def<'a> {
|
||||
fn from(def: ValueDef<'a>) -> Self {
|
||||
Self::Value(def)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ast::{
|
||||
AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Has, Pattern, Spaceable,
|
||||
TypeAnnotation, TypeHeader,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::{lowercase_ident, parse_ident, Ident};
|
||||
|
@ -10,7 +10,7 @@ use crate::parser::{
|
|||
trailing_sep_by0, word1, word2, EExpect, EExpr, EIf, EInParens, ELambda, EList, ENumber,
|
||||
EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
|
||||
};
|
||||
use crate::pattern::loc_closure_param;
|
||||
use crate::pattern::{loc_closure_param, loc_has_parser};
|
||||
use crate::state::State;
|
||||
use crate::type_annotation;
|
||||
use bumpalo::collections::Vec;
|
||||
|
@ -576,7 +576,7 @@ fn append_body_definition<'a>(
|
|||
if spaces.len() <= 1 {
|
||||
let last = defs.pop();
|
||||
match last.map(|d| d.value.unroll_spaces_before()) {
|
||||
Some((before_ann_spaces, Def::Annotation(ann_pattern, ann_type))) => {
|
||||
Some((before_ann_spaces, Def::Value(ValueDef::Annotation(ann_pattern, ann_type)))) => {
|
||||
return append_body_definition_help(
|
||||
arena,
|
||||
defs,
|
||||
|
@ -591,10 +591,10 @@ fn append_body_definition<'a>(
|
|||
}
|
||||
Some((
|
||||
before_ann_spaces,
|
||||
Def::Alias {
|
||||
Def::Type(TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
},
|
||||
}),
|
||||
)) => {
|
||||
// This is a case like
|
||||
// UserId x : [ UserId Int ]
|
||||
|
@ -628,7 +628,10 @@ fn append_body_definition<'a>(
|
|||
// the previous and current def can't be joined up
|
||||
let mut loc_def = Loc::at(
|
||||
region,
|
||||
Def::Body(arena.alloc(loc_pattern), &*arena.alloc(loc_def_body)),
|
||||
Def::Value(ValueDef::Body(
|
||||
arena.alloc(loc_pattern),
|
||||
&*arena.alloc(loc_def_body),
|
||||
)),
|
||||
);
|
||||
|
||||
if !spaces.is_empty() {
|
||||
|
@ -660,13 +663,13 @@ fn append_body_definition_help<'a>(
|
|||
|
||||
let mut loc_def = Loc::at(
|
||||
region,
|
||||
Def::AnnotatedBody {
|
||||
Def::Value(ValueDef::AnnotatedBody {
|
||||
ann_pattern: loc_pattern_ann,
|
||||
ann_type: loc_ann,
|
||||
comment,
|
||||
body_pattern: arena.alloc(loc_pattern_body),
|
||||
body_expr: &*arena.alloc(loc_def_body),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
if !before_ann_spaces.is_empty() {
|
||||
|
@ -717,7 +720,10 @@ fn append_annotation_definition<'a>(
|
|||
kind,
|
||||
),
|
||||
_ => {
|
||||
let mut loc_def = Loc::at(region, Def::Annotation(loc_pattern, loc_ann));
|
||||
let mut loc_def = Loc::at(
|
||||
region,
|
||||
Def::Value(ValueDef::Annotation(loc_pattern, loc_ann)),
|
||||
);
|
||||
if !spaces.is_empty() {
|
||||
loc_def = arena
|
||||
.alloc(loc_def.value)
|
||||
|
@ -736,7 +742,7 @@ fn append_expect_definition<'a>(
|
|||
spaces: &'a [CommentOrNewline<'a>],
|
||||
loc_expect_body: Loc<Expr<'a>>,
|
||||
) {
|
||||
let def = Def::Expect(arena.alloc(loc_expect_body));
|
||||
let def: Def = ValueDef::Expect(arena.alloc(loc_expect_body)).into();
|
||||
|
||||
let end = loc_expect_body.region.end();
|
||||
let region = Region::new(start, end);
|
||||
|
@ -768,16 +774,16 @@ fn append_type_definition<'a>(
|
|||
vars: pattern_arguments,
|
||||
};
|
||||
let def = match kind {
|
||||
TypeKind::Alias => Def::Alias {
|
||||
TypeKind::Alias => TypeDef::Alias {
|
||||
header,
|
||||
ann: loc_ann,
|
||||
},
|
||||
TypeKind::Opaque => Def::Opaque {
|
||||
TypeKind::Opaque => TypeDef::Opaque {
|
||||
header,
|
||||
typ: loc_ann,
|
||||
},
|
||||
};
|
||||
let mut loc_def = Loc::at(region, def);
|
||||
let mut loc_def = Loc::at(region, Def::Type(def));
|
||||
|
||||
if !spaces.is_empty() {
|
||||
loc_def = arena
|
||||
|
@ -858,7 +864,42 @@ fn parse_defs_end<'a>(
|
|||
// a hacky way to get expression-based error messages. TODO fix this
|
||||
Ok((NoProgress, def_state, initial))
|
||||
}
|
||||
Ok((_, loc_pattern, state)) => match operator().parse(arena, state) {
|
||||
Ok((_, loc_pattern, state)) => {
|
||||
// First let's check whether this is an ability definition.
|
||||
let opt_tag_and_args: Option<(&str, Region, &[Loc<Pattern>])> = match loc_pattern.value
|
||||
{
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::GlobalTag(name),
|
||||
region,
|
||||
},
|
||||
args,
|
||||
) => Some((name, *region, args)),
|
||||
Pattern::GlobalTag(name) => Some((name, loc_pattern.region, &[])),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((name, name_region, args)) = opt_tag_and_args {
|
||||
if let Ok((_, loc_has, state)) =
|
||||
loc_has_parser(min_indent).parse(arena, state.clone())
|
||||
{
|
||||
let (_, loc_def, state) = finish_parsing_ability_def(
|
||||
start_column,
|
||||
Loc::at(name_region, name),
|
||||
args,
|
||||
loc_has,
|
||||
arena,
|
||||
state,
|
||||
)?;
|
||||
|
||||
def_state.defs.push(loc_def);
|
||||
|
||||
return parse_defs_end(options, column, def_state, arena, state);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, this is a def or alias.
|
||||
match operator().parse(arena, state) {
|
||||
Ok((_, BinOp::Assignment, state)) => {
|
||||
let parse_def_expr = space0_before_e(
|
||||
move |a, s| parse_loc_expr(min_indent + 1, a, s),
|
||||
|
@ -906,7 +947,8 @@ fn parse_defs_end<'a>(
|
|||
}
|
||||
|
||||
_ => Ok((MadeProgress, def_state, initial)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1004,17 +1046,17 @@ fn finish_parsing_alias_or_opaque<'a>(
|
|||
vars: type_arguments.into_bump_slice(),
|
||||
};
|
||||
let type_def = match kind {
|
||||
TypeKind::Alias => Def::Alias {
|
||||
TypeKind::Alias => TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
},
|
||||
TypeKind::Opaque => Def::Opaque {
|
||||
TypeKind::Opaque => TypeDef::Opaque {
|
||||
header,
|
||||
typ: ann_type,
|
||||
},
|
||||
};
|
||||
|
||||
(&*arena.alloc(Loc::at(def_region, type_def)), state)
|
||||
(&*arena.alloc(Loc::at(def_region, type_def.into())), state)
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
@ -1043,9 +1085,9 @@ fn finish_parsing_alias_or_opaque<'a>(
|
|||
|
||||
let def_region = Region::span_across(&call.region, &ann_type.region);
|
||||
|
||||
let alias = Def::Annotation(Loc::at(expr_region, good), ann_type);
|
||||
let alias = ValueDef::Annotation(Loc::at(expr_region, good), ann_type);
|
||||
|
||||
(&*arena.alloc(Loc::at(def_region, alias)), state)
|
||||
(&*arena.alloc(Loc::at(def_region, alias.into())), state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1074,14 +1116,14 @@ fn finish_parsing_alias_or_opaque<'a>(
|
|||
mod ability {
|
||||
use super::*;
|
||||
use crate::{
|
||||
ast::{AbilityDemand, Spaceable, Spaced},
|
||||
ast::{AbilityMember, Spaceable, Spaced},
|
||||
parser::EAbility,
|
||||
};
|
||||
|
||||
/// Parses a single ability demand line; see `parse_demand`.
|
||||
fn parse_demand_help<'a>(
|
||||
start_column: u32,
|
||||
) -> impl Parser<'a, AbilityDemand<'a>, EAbility<'a>> {
|
||||
) -> impl Parser<'a, AbilityMember<'a>, EAbility<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
specialize(|_, pos| EAbility::DemandName(pos), loc!(lowercase_ident())),
|
||||
|
@ -1099,7 +1141,7 @@ mod ability {
|
|||
)
|
||||
),
|
||||
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| {
|
||||
AbilityDemand {
|
||||
AbilityMember {
|
||||
name: name.map_owned(Spaced::Item),
|
||||
typ,
|
||||
}
|
||||
|
@ -1117,7 +1159,7 @@ mod ability {
|
|||
/// This is basically the same as parsing a free-floating annotation, but with stricter rules.
|
||||
pub fn parse_demand<'a>(
|
||||
indent: IndentLevel,
|
||||
) -> impl Parser<'a, (u32, AbilityDemand<'a>), EAbility<'a>> {
|
||||
) -> impl Parser<'a, (u32, AbilityMember<'a>), EAbility<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let initial = state.clone();
|
||||
|
||||
|
@ -1190,15 +1232,14 @@ mod ability {
|
|||
}
|
||||
}
|
||||
|
||||
fn finish_parsing_ability<'a>(
|
||||
fn finish_parsing_ability_def<'a>(
|
||||
start_column: u32,
|
||||
options: ExprParseOptions,
|
||||
name: Loc<&'a str>,
|
||||
args: &'a [Loc<Pattern<'a>>],
|
||||
loc_has: Loc<Has<'a>>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
) -> ParseResult<'a, &'a Loc<Def<'a>>, EExpr<'a>> {
|
||||
let mut demands = Vec::with_capacity_in(2, arena);
|
||||
|
||||
let min_indent_for_demand = start_column + 1;
|
||||
|
@ -1237,13 +1278,29 @@ fn finish_parsing_ability<'a>(
|
|||
}
|
||||
|
||||
let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region);
|
||||
let def = Def::Ability {
|
||||
let def = TypeDef::Ability {
|
||||
header: TypeHeader { name, vars: args },
|
||||
loc_has,
|
||||
demands: demands.into_bump_slice(),
|
||||
};
|
||||
members: demands.into_bump_slice(),
|
||||
}
|
||||
.into();
|
||||
let loc_def = &*(arena.alloc(Loc::at(def_region, def)));
|
||||
|
||||
Ok((MadeProgress, loc_def, state))
|
||||
}
|
||||
|
||||
fn finish_parsing_ability<'a>(
|
||||
start_column: u32,
|
||||
options: ExprParseOptions,
|
||||
name: Loc<&'a str>,
|
||||
args: &'a [Loc<Pattern<'a>>],
|
||||
loc_has: Loc<Has<'a>>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let (_, loc_def, state) =
|
||||
finish_parsing_ability_def(start_column, name, args, loc_has, arena, state)?;
|
||||
|
||||
let def_state = DefState {
|
||||
defs: bumpalo::vec![in arena; loc_def],
|
||||
spaces_after: &[],
|
||||
|
@ -1309,23 +1366,24 @@ fn parse_expr_operator<'a>(
|
|||
let (loc_def, state) = {
|
||||
match expr_to_pattern_help(arena, &call.value) {
|
||||
Ok(good) => {
|
||||
let (_, mut ann_type, state) = parse_loc_expr(indented_more, arena, state)?;
|
||||
let (_, mut body, state) = parse_loc_expr(indented_more, arena, state)?;
|
||||
|
||||
// put the spaces from after the operator in front of the call
|
||||
if !spaces_after_operator.is_empty() {
|
||||
ann_type = arena
|
||||
.alloc(ann_type.value)
|
||||
.with_spaces_before(spaces_after_operator, ann_type.region);
|
||||
body = arena
|
||||
.alloc(body.value)
|
||||
.with_spaces_before(spaces_after_operator, body.region);
|
||||
}
|
||||
|
||||
let alias_region = Region::span_across(&call.region, &ann_type.region);
|
||||
let body_region = Region::span_across(&call.region, &body.region);
|
||||
|
||||
let alias = Def::Body(
|
||||
let alias = ValueDef::Body(
|
||||
arena.alloc(Loc::at(expr_region, good)),
|
||||
arena.alloc(ann_type),
|
||||
);
|
||||
arena.alloc(body),
|
||||
)
|
||||
.into();
|
||||
|
||||
(&*arena.alloc(Loc::at(alias_region, alias)), state)
|
||||
(&*arena.alloc(Loc::at(body_region, alias)), state)
|
||||
}
|
||||
Err(_) => {
|
||||
// this `=` likely occurred inline; treat it as an invalid operator
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::ast::Pattern;
|
||||
use crate::ast::{Has, Pattern};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::{lowercase_ident, parse_ident, Ident};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
backtrackable, optional, specialize, specialize_ref, word1, EPattern, PInParens, PRecord,
|
||||
backtrackable, optional, specialize, specialize_ref, then, word1, EPattern, PInParens, PRecord,
|
||||
ParseResult, Parser,
|
||||
};
|
||||
use crate::state::State;
|
||||
|
@ -68,20 +68,37 @@ pub fn loc_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
|
|||
fn loc_tag_pattern_args_help<'a>(
|
||||
min_indent: u32,
|
||||
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
|
||||
zero_or_more!(loc_tag_pattern_arg(min_indent))
|
||||
zero_or_more!(loc_tag_pattern_arg(min_indent, false))
|
||||
}
|
||||
|
||||
fn loc_tag_pattern_arg<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
/// Like `loc_tag_pattern_args_help`, but stops if a "has" keyword is seen (indicating an ability).
|
||||
fn loc_type_def_tag_pattern_args_help<'a>(
|
||||
min_indent: u32,
|
||||
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
|
||||
zero_or_more!(loc_tag_pattern_arg(min_indent, true))
|
||||
}
|
||||
|
||||
fn loc_tag_pattern_arg<'a>(
|
||||
min_indent: u32,
|
||||
stop_on_has_kw: bool,
|
||||
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
// Don't parse operators, because they have a higher precedence than function application.
|
||||
// If we encounter one, we're done parsing function args!
|
||||
move |arena, state| {
|
||||
let (_, spaces, state) =
|
||||
backtrackable(space0_e(min_indent, EPattern::IndentStart)).parse(arena, state)?;
|
||||
move |arena, original_state: State<'a>| {
|
||||
let (_, spaces, state) = backtrackable(space0_e(min_indent, EPattern::IndentStart))
|
||||
.parse(arena, original_state.clone())?;
|
||||
|
||||
let (_, loc_pat, state) = loc_parse_tag_pattern_arg(min_indent, arena, state)?;
|
||||
|
||||
let Loc { region, value } = loc_pat;
|
||||
|
||||
if stop_on_has_kw && matches!(value, Pattern::Identifier("has")) {
|
||||
Err((
|
||||
NoProgress,
|
||||
EPattern::End(original_state.pos()),
|
||||
original_state,
|
||||
))
|
||||
} else {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
if spaces.is_empty() {
|
||||
|
@ -92,6 +109,20 @@ fn loc_tag_pattern_arg<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>,
|
|||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loc_has_parser<'a>(min_indent: u32) -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> {
|
||||
then(
|
||||
loc_tag_pattern_arg(min_indent, false),
|
||||
|_arena, state, progress, pattern| {
|
||||
if matches!(pattern.value, Pattern::Identifier("has")) {
|
||||
Ok((progress, Loc::at(pattern.region, Has::Has), state))
|
||||
} else {
|
||||
Err((progress, EPattern::End(state.pos()), state))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_parse_tag_pattern_arg<'a>(
|
||||
|
@ -191,7 +222,7 @@ fn loc_ident_pattern_help<'a>(
|
|||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||
if can_have_arguments {
|
||||
let (_, loc_args, state) =
|
||||
loc_tag_pattern_args_help(min_indent).parse(arena, state)?;
|
||||
loc_type_def_tag_pattern_args_help(min_indent).parse(arena, state)?;
|
||||
|
||||
if loc_args.is_empty() {
|
||||
Ok((MadeProgress, loc_tag, state))
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
Defs(
|
||||
[
|
||||
@0-36 Ability {
|
||||
@0-36 Type(
|
||||
Ability {
|
||||
header: TypeHeader {
|
||||
name: @0-4 "Hash",
|
||||
vars: [],
|
||||
},
|
||||
loc_has: @5-8 Has,
|
||||
demands: [
|
||||
AbilityDemand {
|
||||
members: [
|
||||
AbilityMember {
|
||||
name: @11-15 SpaceBefore(
|
||||
"hash",
|
||||
[
|
||||
|
@ -29,6 +30,7 @@ Defs(
|
|||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
@38-39 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
Defs(
|
||||
[
|
||||
@0-45 Ability {
|
||||
@0-45 Type(
|
||||
Ability {
|
||||
header: TypeHeader {
|
||||
name: @0-4 "Hash",
|
||||
vars: [],
|
||||
},
|
||||
loc_has: @5-8 Has,
|
||||
demands: [
|
||||
AbilityDemand {
|
||||
members: [
|
||||
AbilityMember {
|
||||
name: @11-15 SpaceBefore(
|
||||
"hash",
|
||||
[
|
||||
|
@ -27,7 +28,7 @@ Defs(
|
|||
),
|
||||
),
|
||||
},
|
||||
AbilityDemand {
|
||||
AbilityMember {
|
||||
name: @29-34 SpaceBefore(
|
||||
"hash2",
|
||||
[
|
||||
|
@ -49,6 +50,7 @@ Defs(
|
|||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
@47-48 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
Defs(
|
||||
[
|
||||
@0-24 Ability {
|
||||
@0-37 Type(
|
||||
Ability {
|
||||
header: TypeHeader {
|
||||
name: @0-4 "Hash",
|
||||
vars: [],
|
||||
},
|
||||
loc_has: @5-8 Has,
|
||||
demands: [
|
||||
AbilityDemand {
|
||||
members: [
|
||||
AbilityMember {
|
||||
name: @9-13 "hash",
|
||||
typ: @21-24 Function(
|
||||
typ: @21-37 Where(
|
||||
@21-24 Function(
|
||||
[
|
||||
@16-17 BoundVariable(
|
||||
"a",
|
||||
|
@ -21,11 +23,23 @@ Defs(
|
|||
[],
|
||||
),
|
||||
),
|
||||
[
|
||||
@27-37 HasClause {
|
||||
var: @27-28 "a",
|
||||
ability: @33-37 Apply(
|
||||
"",
|
||||
"Hash",
|
||||
[],
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
@26-27 SpaceBefore(
|
||||
@39-40 SpaceBefore(
|
||||
Num(
|
||||
"1",
|
||||
),
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Hash has hash : a -> U64
|
||||
Hash has hash : a -> U64 | a has Hash
|
||||
|
||||
1
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
Defs(
|
||||
[
|
||||
@0-33 Type(
|
||||
Ability {
|
||||
header: TypeHeader {
|
||||
name: @0-3 "Ab1",
|
||||
vars: [],
|
||||
},
|
||||
loc_has: @4-7 Has,
|
||||
members: [
|
||||
AbilityMember {
|
||||
name: @8-11 "ab1",
|
||||
typ: @19-33 Where(
|
||||
@19-21 Function(
|
||||
[
|
||||
@14-15 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
@19-21 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
),
|
||||
[
|
||||
@24-33 HasClause {
|
||||
var: @24-25 "a",
|
||||
ability: @30-33 Apply(
|
||||
"",
|
||||
"Ab1",
|
||||
[],
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
@35-68 Type(
|
||||
Ability {
|
||||
header: TypeHeader {
|
||||
name: @35-38 "Ab2",
|
||||
vars: [],
|
||||
},
|
||||
loc_has: @39-42 Has,
|
||||
members: [
|
||||
AbilityMember {
|
||||
name: @43-46 "ab2",
|
||||
typ: @54-68 Where(
|
||||
@54-56 Function(
|
||||
[
|
||||
@49-50 BoundVariable(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
@54-56 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
),
|
||||
[
|
||||
@59-68 HasClause {
|
||||
var: @59-60 "a",
|
||||
ability: @65-68 Apply(
|
||||
"",
|
||||
"Ab2",
|
||||
[],
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
@70-71 SpaceBefore(
|
||||
Num(
|
||||
"1",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
)
|
|
@ -0,0 +1,5 @@
|
|||
Ab1 has ab1 : a -> {} | a has Ab1
|
||||
|
||||
Ab2 has ab2 : a -> {} | a has Ab2
|
||||
|
||||
1
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@15-49 AnnotatedBody {
|
||||
@15-49 Value(
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-8 RecordDestructure(
|
||||
[
|
||||
@2-3 Identifier(
|
||||
|
@ -48,6 +49,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
@51-52 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@26-46 AnnotatedBody {
|
||||
@26-46 Value(
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-8 Apply(
|
||||
@0-6 GlobalTag(
|
||||
"UserId",
|
||||
|
@ -49,6 +50,7 @@ Defs(
|
|||
Space,
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
@48-49 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@107-112 Body(
|
||||
@107-112 Value(
|
||||
Body(
|
||||
@107-108 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -9,6 +10,7 @@ SpaceBefore(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@114-116 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[
|
||||
@0-7 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@0-3 Identifier(
|
||||
"foo",
|
||||
|
@ -9,6 +10,7 @@
|
|||
"1",
|
||||
),
|
||||
),
|
||||
),
|
||||
[],
|
||||
),
|
||||
[
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-36 Body(
|
||||
@0-36 Value(
|
||||
Body(
|
||||
@0-5 Apply(
|
||||
@0-5 GlobalTag(
|
||||
"Email",
|
||||
|
@ -25,6 +26,7 @@ Defs(
|
|||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@37-40 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-6 Body(
|
||||
@0-6 Value(
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"iffy",
|
||||
),
|
||||
|
@ -8,6 +9,7 @@ Defs(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@8-10 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-58 Body(
|
||||
@0-58 Value(
|
||||
Body(
|
||||
@0-7 Malformed(
|
||||
"my_list",
|
||||
),
|
||||
|
@ -62,6 +63,7 @@ Defs(
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@59-61 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-26 Body(
|
||||
@0-26 Value(
|
||||
Body(
|
||||
@0-7 Malformed(
|
||||
"my_list",
|
||||
),
|
||||
|
@ -30,6 +31,7 @@ Defs(
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@27-29 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-27 Body(
|
||||
@0-27 Value(
|
||||
Body(
|
||||
@0-7 Malformed(
|
||||
"my_list",
|
||||
),
|
||||
|
@ -30,6 +31,7 @@ Defs(
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@28-30 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@113-118 Body(
|
||||
@113-118 Value(
|
||||
Body(
|
||||
@113-114 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -9,6 +10,7 @@ SpaceBefore(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@120-122 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[
|
||||
@0-24 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"main",
|
||||
|
@ -8,7 +9,8 @@
|
|||
@11-24 SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@11-17 Body(
|
||||
@11-17 Value(
|
||||
Body(
|
||||
@11-12 Identifier(
|
||||
"i",
|
||||
),
|
||||
|
@ -16,6 +18,7 @@
|
|||
"64",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@23-24 SpaceBefore(
|
||||
Var {
|
||||
|
@ -33,6 +36,7 @@
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
[],
|
||||
),
|
||||
[
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-10 Annotation(
|
||||
@0-10 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -14,6 +15,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@12-14 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-19 Annotation(
|
||||
@0-19 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -16,6 +17,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@21-23 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[
|
||||
@0-115 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"main",
|
||||
|
@ -8,7 +9,8 @@
|
|||
@11-115 SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@43-93 AnnotatedBody {
|
||||
@43-93 Value(
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-23 Identifier(
|
||||
"wrappedNotEq",
|
||||
),
|
||||
|
@ -62,6 +64,7 @@
|
|||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
@99-115 SpaceBefore(
|
||||
Apply(
|
||||
|
@ -90,6 +93,7 @@
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
[],
|
||||
),
|
||||
[
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-9 Body(
|
||||
@0-9 Value(
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -13,6 +14,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@11-13 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-13 Body(
|
||||
@0-13 Value(
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -23,6 +24,7 @@ Defs(
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@15-17 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@46-51 Body(
|
||||
@46-51 Value(
|
||||
Body(
|
||||
@46-47 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -9,6 +10,7 @@ SpaceBefore(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@53-55 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@18-21 Body(
|
||||
@18-21 Value(
|
||||
Body(
|
||||
@18-19 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -9,6 +10,7 @@ SpaceBefore(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@23-25 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@18-23 Body(
|
||||
@18-23 Value(
|
||||
Body(
|
||||
@18-19 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -9,6 +10,7 @@ SpaceBefore(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@25-27 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[
|
||||
@0-9 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Type(
|
||||
Opaque {
|
||||
header: TypeHeader {
|
||||
name: @0-3 "Age",
|
||||
|
@ -12,6 +13,7 @@
|
|||
[],
|
||||
),
|
||||
},
|
||||
),
|
||||
[],
|
||||
),
|
||||
[
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[
|
||||
@0-53 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Type(
|
||||
Opaque {
|
||||
header: TypeHeader {
|
||||
name: @0-10 "Bookmark",
|
||||
|
@ -41,6 +42,7 @@
|
|||
ext: None,
|
||||
},
|
||||
},
|
||||
),
|
||||
[],
|
||||
),
|
||||
[
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-26 Alias {
|
||||
@0-26 Type(
|
||||
Alias {
|
||||
header: TypeHeader {
|
||||
name: @0-4 "Blah",
|
||||
vars: [
|
||||
|
@ -25,6 +26,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
@28-30 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-33 Annotation(
|
||||
@0-33 Value(
|
||||
Annotation(
|
||||
@0-3 Identifier(
|
||||
"foo",
|
||||
),
|
||||
|
@ -31,6 +32,7 @@ Defs(
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@35-37 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@18-30 Body(
|
||||
@18-30 Value(
|
||||
Body(
|
||||
@18-26 RecordDestructure(
|
||||
[
|
||||
@20-21 Identifier(
|
||||
|
@ -16,7 +17,9 @@ SpaceBefore(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
@31-36 SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@31-32 Identifier(
|
||||
"y",
|
||||
|
@ -25,6 +28,7 @@ SpaceBefore(
|
|||
"6",
|
||||
),
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-122 Annotation(
|
||||
@0-122 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -99,6 +100,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@124-126 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[
|
||||
@12-19 SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@12-15 Identifier(
|
||||
"foo",
|
||||
|
@ -8,6 +9,7 @@
|
|||
"1",
|
||||
),
|
||||
),
|
||||
),
|
||||
[
|
||||
LineComment(
|
||||
" comment 1",
|
||||
|
@ -15,6 +17,7 @@
|
|||
],
|
||||
),
|
||||
@33-43 SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@33-36 Identifier(
|
||||
"bar",
|
||||
|
@ -25,6 +28,7 @@
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
Newline,
|
||||
|
@ -35,6 +39,7 @@
|
|||
),
|
||||
@44-57 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@44-47 Identifier(
|
||||
"baz",
|
||||
|
@ -45,6 +50,7 @@
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
SpaceBefore(
|
||||
Defs(
|
||||
[
|
||||
@18-23 Body(
|
||||
@18-23 Value(
|
||||
Body(
|
||||
@18-19 Identifier(
|
||||
"x",
|
||||
),
|
||||
|
@ -9,7 +10,9 @@ SpaceBefore(
|
|||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
@24-29 SpaceBefore(
|
||||
Value(
|
||||
Body(
|
||||
@24-25 Identifier(
|
||||
"y",
|
||||
|
@ -18,6 +21,7 @@ SpaceBefore(
|
|||
"6",
|
||||
),
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-30 Annotation(
|
||||
@0-30 Value(
|
||||
Annotation(
|
||||
@0-7 Identifier(
|
||||
"doStuff",
|
||||
),
|
||||
|
@ -26,6 +27,7 @@ Defs(
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@31-33 SpaceBefore(
|
||||
Num(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-27 Annotation(
|
||||
@0-27 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -34,6 +35,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@29-30 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-48 Annotation(
|
||||
@0-48 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -50,6 +51,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@50-51 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-67 Annotation(
|
||||
@0-67 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -65,6 +66,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@69-70 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-15 Annotation(
|
||||
@0-15 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -20,6 +21,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@17-18 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Defs(
|
||||
[
|
||||
@0-29 Annotation(
|
||||
@0-29 Value(
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
|
@ -34,6 +35,7 @@ Defs(
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@31-32 SpaceBefore(
|
||||
Var {
|
||||
|
|
|
@ -126,6 +126,7 @@ mod test_parse {
|
|||
pass/ability_single_line.expr,
|
||||
pass/ability_multi_line.expr,
|
||||
pass/ability_demand_signature_is_multiline.expr,
|
||||
pass/ability_two_in_a_row.expr,
|
||||
pass/add_var_with_spaces.expr,
|
||||
pass/add_with_spaces.expr,
|
||||
pass/annotated_record_destructure.expr,
|
||||
|
|
|
@ -20,6 +20,14 @@ pub enum BadPattern {
|
|||
Unsupported(PatternType),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ShadowKind {
|
||||
Variable,
|
||||
Alias,
|
||||
Opaque,
|
||||
Ability,
|
||||
}
|
||||
|
||||
/// Problems that can occur in the course of canonicalization.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Problem {
|
||||
|
@ -33,9 +41,10 @@ pub enum Problem {
|
|||
PrecedenceProblem(PrecedenceProblem),
|
||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||
UnsupportedPattern(BadPattern, Region),
|
||||
ShadowingInAnnotation {
|
||||
Shadowing {
|
||||
original_region: Region,
|
||||
shadow: Loc<Ident>,
|
||||
kind: ShadowKind,
|
||||
},
|
||||
CyclicAlias(Symbol, Region, Vec<Symbol>),
|
||||
BadRecursion(Vec<CycleEntry>),
|
||||
|
@ -95,6 +104,30 @@ pub enum Problem {
|
|||
region: Region,
|
||||
kind: ExtensionTypeKind,
|
||||
},
|
||||
AbilityHasTypeVariables {
|
||||
name: Symbol,
|
||||
variables_region: Region,
|
||||
},
|
||||
HasClauseIsNotAbility {
|
||||
region: Region,
|
||||
},
|
||||
IllegalHasClause {
|
||||
region: Region,
|
||||
},
|
||||
AbilityMemberMissingHasClause {
|
||||
member: Symbol,
|
||||
ability: Symbol,
|
||||
region: Region,
|
||||
},
|
||||
AbilityMemberBindsExternalAbility {
|
||||
member: Symbol,
|
||||
ability: Symbol,
|
||||
region: Region,
|
||||
},
|
||||
AliasUsesAbility {
|
||||
loc_name: Loc<Symbol>,
|
||||
ability: Symbol,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -157,6 +190,7 @@ pub enum RuntimeError {
|
|||
Shadowing {
|
||||
original_region: Region,
|
||||
shadow: Loc<Ident>,
|
||||
kind: ShadowKind,
|
||||
},
|
||||
InvalidOptionalValue {
|
||||
field_name: Lowercase,
|
||||
|
|
|
@ -5271,6 +5271,21 @@ mod solve_expr {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_float() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
{
|
||||
toF32: Num.toF32,
|
||||
toF64: Num.toF64,
|
||||
}
|
||||
"#
|
||||
),
|
||||
r#"{ toF32 : Num * -> F32, toF64 : Num * -> F64 }"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opaque_wrap_infer() {
|
||||
infer_eq_without_problem(
|
||||
|
@ -5622,4 +5637,29 @@ mod solve_expr {
|
|||
r#"Outer"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2583_specialize_errors_behind_unified_branches() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
if True then List.first [] else Str.toI64 ""
|
||||
"#
|
||||
),
|
||||
"Result I64 [ InvalidNumStr, ListWasEmpty ]*",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lots_of_type_variables() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
fun = \a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa,bb -> {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa,bb}
|
||||
fun
|
||||
"#
|
||||
),
|
||||
"a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb -> { a : a, aa : aa, b : b, bb : bb, c : c, d : d, e : e, f : f, g : g, h : h, i : i, j : j, k : k, l : l, m : m, n : n, o : o, p : p, q : q, r : r, s : s, t : t, u : u, v : v, w : w, x : x, y : y, z : z }",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -954,7 +954,7 @@ fn list_keep_if_str_is_hello() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_on_empty_list_with_int_layout() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -972,7 +972,7 @@ fn list_map_on_empty_list_with_int_layout() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_on_non_empty_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -990,7 +990,7 @@ fn list_map_on_non_empty_list() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_changes_input() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1008,7 +1008,7 @@ fn list_map_changes_input() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_on_big_list() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1028,7 +1028,7 @@ fn list_map_on_big_list() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_with_type_change() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1047,7 +1047,7 @@ fn list_map_with_type_change() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_using_defined_function() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -1069,7 +1069,7 @@ fn list_map_using_defined_function() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn list_map_all_inline() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
|
|
@ -2160,7 +2160,7 @@ fn max_u8() {
|
|||
);
|
||||
}
|
||||
|
||||
macro_rules! to_int_tests {
|
||||
macro_rules! num_conversion_tests {
|
||||
($($fn:expr, $typ:ty, ($($test_name:ident, $input:expr, $output:expr $(, [ $($support_gen:literal),* ])? )*))*) => {$($(
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", $($(feature = $support_gen)*)?))]
|
||||
|
@ -2171,7 +2171,7 @@ macro_rules! to_int_tests {
|
|||
)*)*}
|
||||
}
|
||||
|
||||
to_int_tests! {
|
||||
num_conversion_tests! {
|
||||
"Num.toI8", i8, (
|
||||
to_i8_same_width, "15u8", 15, ["gen-wasm"]
|
||||
to_i8_truncate, "115i32", 115, ["gen-wasm"]
|
||||
|
@ -2232,6 +2232,36 @@ to_int_tests! {
|
|||
to_nat_truncate, "115i128", 115
|
||||
to_nat_truncate_wraps, "10_000_000_000_000_000_000_000i128", 1864712049423024128
|
||||
)
|
||||
"Num.toF32", f32, (
|
||||
to_f32_from_i8, "15i8", 15.0
|
||||
to_f32_from_i16, "15i16", 15.0
|
||||
to_f32_from_i32, "15i32", 15.0
|
||||
to_f32_from_i64, "15i64", 15.0
|
||||
to_f32_from_i128, "15i128", 15.0
|
||||
to_f32_from_u8, "15u8", 15.0
|
||||
to_f32_from_u16, "15u16", 15.0
|
||||
to_f32_from_u32, "15u32", 15.0
|
||||
to_f32_from_u64, "15u64", 15.0
|
||||
to_f32_from_u128, "15u128", 15.0
|
||||
to_f32_from_nat, "15nat", 15.0
|
||||
to_f32_from_f32, "1.5f32", 1.5
|
||||
to_f32_from_f64, "1.5f64", 1.5
|
||||
)
|
||||
"Num.toF64", f64, (
|
||||
to_f64_from_i8, "15i8", 15.0
|
||||
to_f64_from_i16, "15i16", 15.0
|
||||
to_f64_from_i32, "15i32", 15.0
|
||||
to_f64_from_i64, "15i64", 15.0
|
||||
to_f64_from_i128, "15i128", 15.0
|
||||
to_f64_from_u8, "15u8", 15.0
|
||||
to_f64_from_u16, "15u16", 15.0
|
||||
to_f64_from_u32, "15u32", 15.0
|
||||
to_f64_from_u64, "15u64", 15.0
|
||||
to_f64_from_u128, "15u128", 15.0
|
||||
to_f64_from_nat, "15nat", 15.0
|
||||
to_f64_from_f32, "1.5f32", 1.5
|
||||
to_f64_from_f64, "1.5f64", 1.5
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! to_int_checked_tests {
|
||||
|
|
|
@ -1074,7 +1074,7 @@ fn closure_in_list() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn specialize_closure() {
|
||||
use roc_std::RocList;
|
||||
|
||||
|
@ -2660,7 +2660,7 @@ fn pattern_match_unit_tag() {
|
|||
// see for why this is disabled on wasm32 https://github.com/rtfeldman/roc/issues/1687
|
||||
#[cfg(not(feature = "wasm-cli-run"))]
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn mirror_llvm_alignment_padding() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1569
|
||||
assert_evals_to!(
|
||||
|
@ -2683,7 +2683,7 @@ fn mirror_llvm_alignment_padding() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn lambda_set_bool() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -2708,7 +2708,7 @@ fn lambda_set_bool() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn lambda_set_byte() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -2762,7 +2762,7 @@ fn lambda_set_struct_byte() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn lambda_set_enum_byte_byte() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -3275,3 +3275,47 @@ fn box_and_unbox_string() {
|
|||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn box_and_unbox_num() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Box.unbox (Box.box (123u8))
|
||||
"#
|
||||
),
|
||||
123,
|
||||
u8
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn box_and_unbox_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Box.unbox (Box.box { a: 15u8, b: 27u8 })
|
||||
"#
|
||||
),
|
||||
(15, 27),
|
||||
(u8, u8)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn box_and_unbox_tag_union() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
v : [ A U8, B U8 ] # usually stack allocated
|
||||
v = B 27u8
|
||||
Box.unbox (Box.box v)
|
||||
"#
|
||||
),
|
||||
(27, 1),
|
||||
(u8, u8)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -301,8 +301,8 @@ fn refcount_different_rosetrees_inc() {
|
|||
(Pointer, Pointer),
|
||||
&[
|
||||
Live(2), // s
|
||||
Live(3), // i1
|
||||
Live(2), // s1
|
||||
Live(3), // i1
|
||||
Live(1), // [i1, i1]
|
||||
Live(1), // i2
|
||||
Live(1), // [s1, s1]
|
||||
|
|
|
@ -256,3 +256,15 @@ fn roc_result_err() {
|
|||
RocResult<i64, RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn issue_2583_specialize_errors_behind_unified_branches() {
|
||||
assert_evals_to!(
|
||||
r#"
|
||||
if True then List.first [15] else Str.toI64 ""
|
||||
"#,
|
||||
RocResult::ok(15i64),
|
||||
RocResult<i64, bool>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1587,3 +1587,19 @@ fn str_to_dec() {
|
|||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn issue_2811() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = Command { tool: "bash" }
|
||||
Command c = x
|
||||
c.tool
|
||||
"#
|
||||
),
|
||||
RocStr::from("bash"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
procedure List.10 (#Attr.2):
|
||||
let Test.20 : U64 = 0i64;
|
||||
let Test.21 : U64 = lowlevel ListLen #Attr.2;
|
||||
let Test.16 : Int1 = lowlevel NotEq Test.20 Test.21;
|
||||
if Test.16 then
|
||||
let Test.19 : U64 = 0i64;
|
||||
let Test.18 : I64 = lowlevel ListGetUnsafe #Attr.2 Test.19;
|
||||
let Test.17 : [C Int1, C I64] = Ok Test.18;
|
||||
ret Test.17;
|
||||
else
|
||||
let Test.15 : Int1 = true;
|
||||
let Test.14 : [C Int1, C I64] = Err Test.15;
|
||||
ret Test.14;
|
||||
|
||||
procedure Str.28 (#Attr.2):
|
||||
let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
||||
let Test.9 : U8 = StructAtIndex 1 #Attr.3;
|
||||
let Test.10 : U8 = 0i64;
|
||||
let Test.6 : Int1 = lowlevel NumGt Test.9 Test.10;
|
||||
if Test.6 then
|
||||
let Test.8 : Int1 = false;
|
||||
let Test.7 : [C Int1, C I64] = Err Test.8;
|
||||
ret Test.7;
|
||||
else
|
||||
let Test.5 : I64 = StructAtIndex 0 #Attr.3;
|
||||
let Test.4 : [C Int1, C I64] = Ok Test.5;
|
||||
ret Test.4;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 : Int1 = true;
|
||||
if Test.11 then
|
||||
let Test.13 : List I64 = Array [];
|
||||
let Test.12 : [C Int1, C I64] = CallByName List.10 Test.13;
|
||||
dec Test.13;
|
||||
ret Test.12;
|
||||
else
|
||||
let Test.3 : Str = "";
|
||||
let Test.2 : [C Int1, C I64] = CallByName Str.28 Test.3;
|
||||
dec Test.3;
|
||||
ret Test.2;
|
3
compiler/test_mono/generated/issue_2811.txt
Normal file
3
compiler/test_mono/generated/issue_2811.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
procedure Test.0 ():
|
||||
let Test.6 : Str = "bash";
|
||||
ret Test.6;
|
|
@ -1273,6 +1273,26 @@ fn issue_2725_alias_polymorphic_lambda() {
|
|||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn issue_2583_specialize_errors_behind_unified_branches() {
|
||||
indoc!(
|
||||
r#"
|
||||
if True then List.first [] else Str.toI64 ""
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn issue_2811() {
|
||||
indoc!(
|
||||
r#"
|
||||
x = Command { tool: "bash" }
|
||||
Command c = x
|
||||
c.tool
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
// #[ignore]
|
||||
// #[mono_test]
|
||||
// fn static_str_closure() {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue