mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Merge remote-tracking branch 'origin/trunk' into div-no-result
This commit is contained in:
commit
8206f345c7
46 changed files with 1685 additions and 291 deletions
2
AUTHORS
2
AUTHORS
|
@ -72,3 +72,5 @@ Elliot Waite <1767836+elliotwaite@users.noreply.github.com>
|
||||||
zimt28 <1764689+zimt28@users.noreply.github.com>
|
zimt28 <1764689+zimt28@users.noreply.github.com>
|
||||||
Ananda Umamil <zweimach@zweimach.org>
|
Ananda Umamil <zweimach@zweimach.org>
|
||||||
SylvanSign <jake.d.bray@gmail.com>
|
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`.
|
To use the `repl` subcommand, execute `cargo run repl`.
|
||||||
Use `cargo build` to build the whole project.
|
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
|
#### 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!
|
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_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
"roc_constrain",
|
||||||
|
"roc_error_macros",
|
||||||
"roc_gen_dev",
|
"roc_gen_dev",
|
||||||
"roc_gen_llvm",
|
"roc_gen_llvm",
|
||||||
"roc_gen_wasm",
|
"roc_gen_wasm",
|
||||||
|
|
5
FAQ.md
5
FAQ.md
|
@ -1,5 +1,10 @@
|
||||||
# Frequently Asked Questions
|
# 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?
|
## 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
|
Not currently. Although they will presumably exist someday, while Roc is in the early days there's actually a conscious
|
||||||
|
|
|
@ -18,7 +18,7 @@ use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast::{self, TypeDef, TypeHeader, ValueDef as AstValueDef};
|
use roc_parse::ast::{self, TypeDef, TypeHeader, ValueDef as AstValueDef};
|
||||||
use roc_parse::pattern::PatternType;
|
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_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -251,9 +251,10 @@ fn to_pending_def<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Err((original_region, loc_shadowed_symbol)) => {
|
Err((original_region, loc_shadowed_symbol)) => {
|
||||||
env.problem(Problem::ShadowingInAnnotation {
|
env.problem(Problem::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: loc_shadowed_symbol,
|
shadow: loc_shadowed_symbol,
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
});
|
});
|
||||||
|
|
||||||
Some((Output::default(), PendingDef::InvalidAlias))
|
Some((Output::default(), PendingDef::InvalidAlias))
|
||||||
|
|
|
@ -12,7 +12,7 @@ use roc_error_macros::todo_opaques;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||||
use roc_parse::pattern::PatternType;
|
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_region::all::Region;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
|
||||||
|
@ -161,6 +161,7 @@ pub fn to_pattern2<'a>(
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let name: &str = shadow.value.as_ref();
|
let name: &str = shadow.value.as_ref();
|
||||||
|
@ -364,6 +365,7 @@ pub fn to_pattern2<'a>(
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// let shadowed = Pattern2::Shadowed {
|
// let shadowed = Pattern2::Shadowed {
|
||||||
|
@ -443,6 +445,7 @@ pub fn to_pattern2<'a>(
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// No matter what the other patterns
|
// No matter what the other patterns
|
||||||
|
|
|
@ -246,6 +246,11 @@ pub fn build_file<'a>(
|
||||||
todo!("gracefully handle failing to surgically link");
|
todo!("gracefully handle failing to surgically link");
|
||||||
})?;
|
})?;
|
||||||
BuildOutcome::NoProblems
|
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 {
|
} else {
|
||||||
let mut inputs = vec![
|
let mut inputs = vec![
|
||||||
host_input_path.as_path().to_str().unwrap(),
|
host_input_path.as_path().to_str().unwrap(),
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub const FLAG_DEV: &str = "dev";
|
||||||
pub const FLAG_OPTIMIZE: &str = "optimize";
|
pub const FLAG_OPTIMIZE: &str = "optimize";
|
||||||
pub const FLAG_OPT_SIZE: &str = "opt-size";
|
pub const FLAG_OPT_SIZE: &str = "opt-size";
|
||||||
pub const FLAG_LIB: &str = "lib";
|
pub const FLAG_LIB: &str = "lib";
|
||||||
|
pub const FLAG_NO_LINK: &str = "no-link";
|
||||||
pub const FLAG_TARGET: &str = "target";
|
pub const FLAG_TARGET: &str = "target";
|
||||||
pub const FLAG_TIME: &str = "time";
|
pub const FLAG_TIME: &str = "time";
|
||||||
pub const FLAG_LINK: &str = "roc-linker";
|
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.")
|
.about("Build a C library instead of an executable.")
|
||||||
.required(false),
|
.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(
|
||||||
Arg::new(FLAG_DEBUG)
|
Arg::new(FLAG_DEBUG)
|
||||||
.long(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_debug_info = matches.is_present(FLAG_DEBUG);
|
||||||
let emit_timings = matches.is_present(FLAG_TIME);
|
let emit_timings = matches.is_present(FLAG_TIME);
|
||||||
|
|
||||||
let link_type = if matches.is_present(FLAG_LIB) {
|
let link_type = match (
|
||||||
LinkType::Dylib
|
matches.is_present(FLAG_LIB),
|
||||||
} else {
|
matches.is_present(FLAG_NO_LINK),
|
||||||
LinkType::Executable
|
) {
|
||||||
|
(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 surgically_link = matches.is_present(FLAG_LINK);
|
||||||
let precompiled = matches.is_present(FLAG_PRECOMPILED);
|
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_wasm = { path = "../gen_wasm", optional = true }
|
||||||
roc_gen_dev = { path = "../gen_dev", default-features = false }
|
roc_gen_dev = { path = "../gen_dev", default-features = false }
|
||||||
roc_reporting = { path = "../../reporting" }
|
roc_reporting = { path = "../../reporting" }
|
||||||
|
roc_error_macros = { path = "../../error_macros" }
|
||||||
roc_std = { path = "../../roc_std", default-features = false }
|
roc_std = { path = "../../roc_std", default-features = false }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
libloading = "0.7.1"
|
libloading = "0.7.1"
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::target::{arch_str, target_zig_str};
|
||||||
#[cfg(feature = "llvm")]
|
#[cfg(feature = "llvm")]
|
||||||
use libloading::{Error, Library};
|
use libloading::{Error, Library};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
// #[cfg(feature = "llvm")]
|
// #[cfg(feature = "llvm")]
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -20,10 +21,10 @@ fn zig_executable() -> String {
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum LinkType {
|
pub enum LinkType {
|
||||||
// These numbers correspond to the --lib flag; if it's present
|
// These numbers correspond to the --lib and --no-link flags
|
||||||
// (e.g. is_present returns `1 as bool`), this will be 1 as well.
|
|
||||||
Executable = 0,
|
Executable = 0,
|
||||||
Dylib = 1,
|
Dylib = 1,
|
||||||
|
None = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// input_paths can include the host as well as the app. e.g. &["host.o", "roc_app.o"]
|
/// 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,
|
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());
|
let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string());
|
||||||
|
@ -904,6 +906,7 @@ fn link_macos(
|
||||||
|
|
||||||
("-dylib", output_path)
|
("-dylib", output_path)
|
||||||
}
|
}
|
||||||
|
LinkType::None => internal_error!("link_macos should not be called with link type of none"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let arch = match target.architecture {
|
let arch = match target.architecture {
|
||||||
|
|
|
@ -628,6 +628,15 @@ toU16 : Int * -> U16
|
||||||
toU32 : Int * -> U32
|
toU32 : Int * -> U32
|
||||||
toU64 : Int * -> U64
|
toU64 : Int * -> U64
|
||||||
toU128 : Int * -> U128
|
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.
|
## Convert any [Int] to a specifically-sized [Int], after checking validity.
|
||||||
## These are checked bitwise operations,
|
## These are checked bitwise operations,
|
||||||
## so if the source number is outside the target range, then these will
|
## 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 ]*
|
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
||||||
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
||||||
|
|
||||||
|
toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
|
||||||
|
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
|
||||||
|
|
||||||
## Convert a number to a [Str].
|
## Convert a number to a [Str].
|
||||||
##
|
##
|
||||||
## This is the same as calling `Num.format {}` - so for more details on
|
## This is the same as calling `Num.format {}` - so for more details on
|
||||||
|
@ -765,14 +777,6 @@ toU32 : Int * -> U32
|
||||||
toU64 : Int * -> U64
|
toU64 : Int * -> U64
|
||||||
toU128 : Int * -> U128
|
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,
|
## 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.
|
## there will be a loss of precision.
|
||||||
toDec : Num * -> Dec
|
toDec : Num * -> Dec
|
||||||
|
|
|
@ -342,6 +342,9 @@ toU32 : Int * -> U32
|
||||||
toU64 : Int * -> U64
|
toU64 : Int * -> U64
|
||||||
toU128 : Int * -> U128
|
toU128 : Int * -> U128
|
||||||
|
|
||||||
|
toF32 : Num * -> F32
|
||||||
|
toF64 : Num * -> F64
|
||||||
|
|
||||||
toI8Checked : Int * -> Result I8 [ OutOfBounds ]*
|
toI8Checked : Int * -> Result I8 [ OutOfBounds ]*
|
||||||
toI16Checked : Int * -> Result I16 [ OutOfBounds ]*
|
toI16Checked : Int * -> Result I16 [ OutOfBounds ]*
|
||||||
toI32Checked : Int * -> Result I32 [ OutOfBounds ]*
|
toI32Checked : Int * -> Result I32 [ OutOfBounds ]*
|
||||||
|
@ -352,3 +355,5 @@ toU16Checked : Int * -> Result U16 [ OutOfBounds ]*
|
||||||
toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
|
toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
|
||||||
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
||||||
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
||||||
|
toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
|
||||||
|
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
|
||||||
|
|
|
@ -629,7 +629,35 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::NUM_TO_NAT_CHECKED,
|
Symbol::NUM_TO_NAT_CHECKED,
|
||||||
vec![int_type(flex(TVAR1))],
|
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
|
// 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::env::Env;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
|
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
|
||||||
use roc_error_macros::todo_abilities;
|
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
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_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
|
@ -104,6 +104,10 @@ impl IntroducedVariables {
|
||||||
.find(|nv| nv.variable == var)
|
.find(|nv| nv.variable == var)
|
||||||
.map(|nv| &nv.name)
|
.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) {
|
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(
|
fn make_apply_symbol(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
@ -271,7 +347,13 @@ pub fn find_type_def_symbols(
|
||||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||||
stack.push(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(_) => {}
|
Inferred | Wildcard | Malformed(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -449,9 +531,10 @@ fn can_annotation_help(
|
||||||
Err((original_region, shadow, _new_symbol)) => {
|
Err((original_region, shadow, _new_symbol)) => {
|
||||||
let problem = Problem::Shadowed(original_region, shadow.clone());
|
let problem = Problem::Shadowed(original_region, shadow.clone());
|
||||||
|
|
||||||
env.problem(roc_problem::can::Problem::ShadowingInAnnotation {
|
env.problem(roc_problem::can::Problem::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow,
|
shadow,
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Type::Erroneous(problem);
|
return Type::Erroneous(problem);
|
||||||
|
@ -685,7 +768,17 @@ fn can_annotation_help(
|
||||||
|
|
||||||
Type::Variable(var)
|
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(string) => {
|
||||||
malformed(env, region, 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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn can_extension_type<'a>(
|
fn can_extension_type<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
|
|
|
@ -269,6 +269,10 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
NUM_TO_U128_CHECKED => num_to_u128_checked,
|
NUM_TO_U128_CHECKED => num_to_u128_checked,
|
||||||
NUM_TO_NAT => num_to_nat,
|
NUM_TO_NAT => num_to_nat,
|
||||||
NUM_TO_NAT_CHECKED => num_to_nat_checked,
|
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,
|
NUM_TO_STR => num_to_str,
|
||||||
RESULT_MAP => result_map,
|
RESULT_MAP => result_map,
|
||||||
RESULT_MAP_ERR => result_map_err,
|
RESULT_MAP_ERR => result_map_err,
|
||||||
|
@ -485,6 +489,18 @@ fn num_to_nat(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
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 {
|
fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) -> Def {
|
||||||
let bool_var = var_store.fresh();
|
let bool_var = var_store.fresh();
|
||||||
let num_var_1 = var_store.fresh();
|
let num_var_1 = var_store.fresh();
|
||||||
|
@ -592,6 +608,8 @@ num_to_checked! {
|
||||||
num_to_u64_checked
|
num_to_u64_checked
|
||||||
num_to_u128_checked
|
num_to_u128_checked
|
||||||
num_to_nat_checked
|
num_to_nat_checked
|
||||||
|
num_to_f32_checked
|
||||||
|
num_to_f64_checked
|
||||||
}
|
}
|
||||||
|
|
||||||
// Num.toStr : Num a -> Str
|
// Num.toStr : Num a -> Str
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
|
use crate::abilities::AbilitiesStore;
|
||||||
use crate::annotation::canonicalize_annotation;
|
use crate::annotation::canonicalize_annotation;
|
||||||
|
use crate::annotation::canonicalize_annotation_with_possible_clauses;
|
||||||
use crate::annotation::IntroducedVariables;
|
use crate::annotation::IntroducedVariables;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::expr::ClosureData;
|
use crate::expr::ClosureData;
|
||||||
use crate::expr::Expr::{self, *};
|
use crate::expr::Expr::{self, *};
|
||||||
use crate::expr::{canonicalize_expr, local_successors_with_duplicates, Output, Recursive};
|
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::procedure::References;
|
||||||
use crate::scope::create_alias;
|
use crate::scope::create_alias;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::all::ImSet;
|
use roc_collections::all::ImSet;
|
||||||
use roc_collections::all::{default_hasher, ImEntry, ImMap, MutMap, MutSet, SendMap};
|
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::ident::Lowercase;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast;
|
use roc_parse::ast;
|
||||||
|
use roc_parse::ast::AbilityMember;
|
||||||
|
use roc_parse::ast::ExtractSpaces;
|
||||||
use roc_parse::ast::TypeHeader;
|
use roc_parse::ast::TypeHeader;
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
|
use roc_problem::can::ShadowKind;
|
||||||
use roc_problem::can::{CycleEntry, Problem, RuntimeError};
|
use roc_problem::can::{CycleEntry, Problem, RuntimeError};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
|
@ -86,10 +90,19 @@ enum PendingTypeDef<'a> {
|
||||||
kind: AliasKind,
|
kind: AliasKind,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Ability {
|
||||||
|
name: Loc<Symbol>,
|
||||||
|
members: &'a [ast::AbilityMember<'a>],
|
||||||
|
},
|
||||||
|
|
||||||
/// An invalid alias, that is ignored in the rest of the pipeline
|
/// An invalid alias, that is ignored in the rest of the pipeline
|
||||||
/// e.g. a shadowed alias, or a definition like `MyAlias 1 : Int`
|
/// e.g. a shadowed alias, or a definition like `MyAlias 1 : Int`
|
||||||
/// with an incorrect pattern
|
/// with an incorrect pattern
|
||||||
InvalidAlias { kind: AliasKind },
|
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.
|
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||||
|
@ -239,9 +252,19 @@ pub fn canonicalize_defs<'a>(
|
||||||
env.home.register_debug_idents(&env.ident_ids);
|
env.home.register_debug_idents(&env.ident_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut aliases = SendMap::default();
|
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();
|
let mut referenced_type_symbols = MutMap::default();
|
||||||
|
|
||||||
for pending_def in pending_type_defs.into_iter() {
|
for pending_def in pending_type_defs.into_iter() {
|
||||||
|
@ -260,93 +283,137 @@ pub fn canonicalize_defs<'a>(
|
||||||
|
|
||||||
referenced_type_symbols.insert(name.value, referenced_symbols);
|
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 */
|
||||||
}
|
}
|
||||||
PendingTypeDef::InvalidAlias { .. } => { /* ignore */ }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sorted = sort_type_defs_before_introduction(referenced_type_symbols);
|
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 {
|
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 symbol = name.value;
|
// Does this alias reference any abilities? For now, we don't permit that.
|
||||||
let can_ann = canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
|
let ability_references = can_ann
|
||||||
|
.references
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&ty_ref| abilities_in_scope.iter().find(|&&name| name == ty_ref))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Record all the annotation's references in output.references.lookups
|
if let Some(one_ability_ref) = ability_references.first() {
|
||||||
for symbol in can_ann.references {
|
env.problem(Problem::AliasUsesAbility {
|
||||||
output.references.type_lookups.insert(symbol);
|
loc_name: name,
|
||||||
output.references.referenced_type_defs.insert(symbol);
|
ability: **one_ability_ref,
|
||||||
}
|
|
||||||
|
|
||||||
let mut can_vars: Vec<Loc<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
|
|
||||||
let mut is_phantom = false;
|
|
||||||
|
|
||||||
let mut named = can_ann.introduced_variables.named;
|
|
||||||
for loc_lowercase in vars.iter() {
|
|
||||||
match named.iter().position(|nv| nv.name == loc_lowercase.value) {
|
|
||||||
Some(index) => {
|
|
||||||
// This is a valid lowercase rigid var for the type def.
|
|
||||||
let named_variable = named.swap_remove(index);
|
|
||||||
|
|
||||||
can_vars.push(Loc {
|
|
||||||
value: (named_variable.name, named_variable.variable),
|
|
||||||
region: loc_lowercase.region,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
None => {
|
|
||||||
is_phantom = true;
|
|
||||||
|
|
||||||
env.problems.push(Problem::PhantomTypeArgument {
|
// Record all the annotation's references in output.references.lookups
|
||||||
|
for symbol in can_ann.references {
|
||||||
|
output.references.type_lookups.insert(symbol);
|
||||||
|
output.references.referenced_type_defs.insert(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut can_vars: Vec<Loc<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
|
||||||
|
let mut is_phantom = false;
|
||||||
|
|
||||||
|
let mut named = can_ann.introduced_variables.named;
|
||||||
|
for loc_lowercase in vars.iter() {
|
||||||
|
match named.iter().position(|nv| nv.name == loc_lowercase.value) {
|
||||||
|
Some(index) => {
|
||||||
|
// This is a valid lowercase rigid var for the type def.
|
||||||
|
let named_variable = named.swap_remove(index);
|
||||||
|
|
||||||
|
can_vars.push(Loc {
|
||||||
|
value: (named_variable.name, named_variable.variable),
|
||||||
|
region: loc_lowercase.region,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
is_phantom = true;
|
||||||
|
|
||||||
|
env.problems.push(Problem::PhantomTypeArgument {
|
||||||
|
typ: symbol,
|
||||||
|
variable_region: loc_lowercase.region,
|
||||||
|
variable_name: loc_lowercase.value.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_phantom {
|
||||||
|
// Bail out
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let IntroducedVariables {
|
||||||
|
wildcards,
|
||||||
|
inferred,
|
||||||
|
..
|
||||||
|
} = can_ann.introduced_variables;
|
||||||
|
let num_unbound = named.len() + wildcards.len() + inferred.len();
|
||||||
|
if num_unbound > 0 {
|
||||||
|
let one_occurrence = named
|
||||||
|
.iter()
|
||||||
|
.map(|nv| Loc::at(nv.first_seen, nv.variable))
|
||||||
|
.chain(wildcards)
|
||||||
|
.chain(inferred)
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.region;
|
||||||
|
|
||||||
|
env.problems.push(Problem::UnboundTypeVariable {
|
||||||
typ: symbol,
|
typ: symbol,
|
||||||
variable_region: loc_lowercase.region,
|
num_unbound,
|
||||||
variable_name: loc_lowercase.value.clone(),
|
one_occurrence,
|
||||||
|
kind,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Bail out
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let alias = create_alias(
|
||||||
|
symbol,
|
||||||
|
name.region,
|
||||||
|
can_vars.clone(),
|
||||||
|
can_ann.typ.clone(),
|
||||||
|
kind,
|
||||||
|
);
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_phantom {
|
|
||||||
// Bail out
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let IntroducedVariables {
|
|
||||||
wildcards,
|
|
||||||
inferred,
|
|
||||||
..
|
|
||||||
} = can_ann.introduced_variables;
|
|
||||||
let num_unbound = named.len() + wildcards.len() + inferred.len();
|
|
||||||
if num_unbound > 0 {
|
|
||||||
let one_occurrence = named
|
|
||||||
.iter()
|
|
||||||
.map(|nv| Loc::at(nv.first_seen, nv.variable))
|
|
||||||
.chain(wildcards)
|
|
||||||
.chain(inferred)
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.region;
|
|
||||||
|
|
||||||
env.problems.push(Problem::UnboundTypeVariable {
|
|
||||||
typ: symbol,
|
|
||||||
num_unbound,
|
|
||||||
one_occurrence,
|
|
||||||
kind,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bail out
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let alias = create_alias(
|
|
||||||
symbol,
|
|
||||||
name.region,
|
|
||||||
can_vars.clone(),
|
|
||||||
can_ann.typ.clone(),
|
|
||||||
kind,
|
|
||||||
);
|
|
||||||
aliases.insert(symbol, alias.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we know the alias dependency graph, we can try to insert recursion variables
|
// Now that we know the alias dependency graph, we can try to insert recursion variables
|
||||||
|
@ -362,6 +429,95 @@ 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,
|
// Now that we have the scope completely assembled, and shadowing resolved,
|
||||||
// we're ready to canonicalize any body exprs.
|
// we're ready to canonicalize any body exprs.
|
||||||
|
|
||||||
|
@ -370,7 +526,14 @@ pub fn canonicalize_defs<'a>(
|
||||||
// once we've finished assembling the entire scope.
|
// once we've finished assembling the entire scope.
|
||||||
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
|
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
|
||||||
for loc_def in value_defs.into_iter() {
|
for loc_def in value_defs.into_iter() {
|
||||||
match to_pending_value_def(env, var_store, loc_def.value, &mut scope, pattern_type) {
|
match to_pending_value_def(
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
loc_def.value,
|
||||||
|
&mut scope,
|
||||||
|
&abilities_store,
|
||||||
|
pattern_type,
|
||||||
|
) {
|
||||||
None => { /* skip */ }
|
None => { /* skip */ }
|
||||||
Some((new_output, pending_def)) => {
|
Some((new_output, pending_def)) => {
|
||||||
// store the top-level defs, used to ensure that closures won't capture them
|
// store the top-level defs, used to ensure that closures won't capture them
|
||||||
|
@ -857,6 +1020,13 @@ fn pattern_to_vars_by_symbol(
|
||||||
vars_by_symbol.insert(*symbol, expr_var);
|
vars_by_symbol.insert(*symbol, expr_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbilityMemberSpecialization {
|
||||||
|
ident,
|
||||||
|
specializes: _,
|
||||||
|
} => {
|
||||||
|
vars_by_symbol.insert(*ident, expr_var);
|
||||||
|
}
|
||||||
|
|
||||||
AppliedTag { arguments, .. } => {
|
AppliedTag { arguments, .. } => {
|
||||||
for (var, nested) in arguments {
|
for (var, nested) in arguments {
|
||||||
pattern_to_vars_by_symbol(vars_by_symbol, &nested.value, *var);
|
pattern_to_vars_by_symbol(vars_by_symbol, &nested.value, *var);
|
||||||
|
@ -981,6 +1151,7 @@ fn canonicalize_pending_value_def<'a>(
|
||||||
Pattern::Shadowed(region, loc_ident, _new_symbol) => RuntimeError::Shadowing {
|
Pattern::Shadowed(region, loc_ident, _new_symbol) => RuntimeError::Shadowing {
|
||||||
original_region: *region,
|
original_region: *region,
|
||||||
shadow: loc_ident.clone(),
|
shadow: loc_ident.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
},
|
},
|
||||||
_ => RuntimeError::NoImplementation,
|
_ => RuntimeError::NoImplementation,
|
||||||
};
|
};
|
||||||
|
@ -1481,10 +1652,10 @@ fn to_pending_type_def<'a>(
|
||||||
header: TypeHeader { name, vars },
|
header: TypeHeader { name, vars },
|
||||||
typ: ann,
|
typ: ann,
|
||||||
} => {
|
} => {
|
||||||
let kind = if matches!(def, Alias { .. }) {
|
let (kind, shadow_kind) = if matches!(def, Alias { .. }) {
|
||||||
AliasKind::Structural
|
(AliasKind::Structural, ShadowKind::Alias)
|
||||||
} else {
|
} else {
|
||||||
AliasKind::Opaque
|
(AliasKind::Opaque, ShadowKind::Opaque)
|
||||||
};
|
};
|
||||||
|
|
||||||
let region = Region::span_across(&name.region, &ann.region);
|
let region = Region::span_across(&name.region, &ann.region);
|
||||||
|
@ -1541,9 +1712,10 @@ fn to_pending_type_def<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Err((original_region, loc_shadowed_symbol, _new_symbol)) => {
|
Err((original_region, loc_shadowed_symbol, _new_symbol)) => {
|
||||||
env.problem(Problem::ShadowingInAnnotation {
|
env.problem(Problem::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: loc_shadowed_symbol,
|
shadow: loc_shadowed_symbol,
|
||||||
|
kind: shadow_kind,
|
||||||
});
|
});
|
||||||
|
|
||||||
Some((Output::default(), PendingTypeDef::InvalidAlias { kind }))
|
Some((Output::default(), PendingTypeDef::InvalidAlias { kind }))
|
||||||
|
@ -1551,40 +1723,56 @@ fn to_pending_type_def<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !vars.is_empty() {
|
||||||
|
// Disallow ability type arguments, at least for now.
|
||||||
|
let variables_region = Region::across_all(vars.iter().map(|v| &v.region));
|
||||||
|
|
||||||
|
env.problem(Problem::AbilityHasTypeVariables {
|
||||||
|
name: name.value,
|
||||||
|
variables_region,
|
||||||
|
});
|
||||||
|
return Some((Output::default(), PendingTypeDef::InvalidAbility));
|
||||||
|
}
|
||||||
|
|
||||||
|
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>(
|
|
||||||
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,
|
|
||||||
scope: &mut Scope,
|
|
||||||
pattern_type: PatternType,
|
|
||||||
) -> (Output, PendingValueDef<'a>) {
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
|
|
||||||
(
|
|
||||||
output,
|
|
||||||
PendingValueDef::TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_pending_value_def<'a>(
|
fn to_pending_value_def<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
def: &'a ast::ValueDef<'a>,
|
def: &'a ast::ValueDef<'a>,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
abilities_store: &AbilitiesStore,
|
||||||
pattern_type: PatternType,
|
pattern_type: PatternType,
|
||||||
) -> Option<(Output, PendingValueDef<'a>)> {
|
) -> Option<(Output, PendingValueDef<'a>)> {
|
||||||
use ast::ValueDef::*;
|
use ast::ValueDef::*;
|
||||||
|
@ -1592,10 +1780,11 @@ fn to_pending_value_def<'a>(
|
||||||
match def {
|
match def {
|
||||||
Annotation(loc_pattern, loc_ann) => {
|
Annotation(loc_pattern, loc_ann) => {
|
||||||
// This takes care of checking for shadowing and adding idents to scope.
|
// 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,
|
env,
|
||||||
var_store,
|
var_store,
|
||||||
scope,
|
scope,
|
||||||
|
abilities_store,
|
||||||
pattern_type,
|
pattern_type,
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
|
@ -1608,10 +1797,11 @@ fn to_pending_value_def<'a>(
|
||||||
}
|
}
|
||||||
Body(loc_pattern, loc_expr) => {
|
Body(loc_pattern, loc_expr) => {
|
||||||
// This takes care of checking for shadowing and adding idents to scope.
|
// 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,
|
env,
|
||||||
var_store,
|
var_store,
|
||||||
scope,
|
scope,
|
||||||
|
abilities_store,
|
||||||
pattern_type,
|
pattern_type,
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
|
@ -1636,14 +1826,21 @@ fn to_pending_value_def<'a>(
|
||||||
//
|
//
|
||||||
// { x, y } : { x : Int, y ? Bool }*
|
// { x, y } : { x : Int, y ? Bool }*
|
||||||
// { x, y ? False } = rec
|
// { x, y ? False } = rec
|
||||||
Some(pending_typed_body(
|
//
|
||||||
|
// This takes care of checking for shadowing and adding idents to scope.
|
||||||
|
let (output, loc_can_pattern) = canonicalize_def_header_pattern(
|
||||||
env,
|
env,
|
||||||
body_pattern,
|
|
||||||
ann_type,
|
|
||||||
body_expr,
|
|
||||||
var_store,
|
var_store,
|
||||||
scope,
|
scope,
|
||||||
|
abilities_store,
|
||||||
pattern_type,
|
pattern_type,
|
||||||
|
&body_pattern.value,
|
||||||
|
body_pattern.region,
|
||||||
|
);
|
||||||
|
|
||||||
|
Some((
|
||||||
|
output,
|
||||||
|
PendingValueDef::TypedBody(body_pattern, loc_can_pattern, ann_type, body_expr),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// the pattern of the annotation does not match the pattern of the body direc
|
// the pattern of the annotation does not match the pattern of the body direc
|
||||||
|
|
|
@ -161,7 +161,6 @@ pub enum Expr {
|
||||||
variant_var: Variable,
|
variant_var: Variable,
|
||||||
ext_var: Variable,
|
ext_var: Variable,
|
||||||
name: TagName,
|
name: TagName,
|
||||||
arguments: Vec<(Variable, Loc<Expr>)>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/// A wrapping of an opaque type, like `$Age 21`
|
/// A wrapping of an opaque type, like `$Age 21`
|
||||||
|
@ -813,7 +812,6 @@ pub fn canonicalize_expr<'a>(
|
||||||
(
|
(
|
||||||
ZeroArgumentTag {
|
ZeroArgumentTag {
|
||||||
name: TagName::Global((*tag).into()),
|
name: TagName::Global((*tag).into()),
|
||||||
arguments: vec![],
|
|
||||||
variant_var,
|
variant_var,
|
||||||
closure_name: symbol,
|
closure_name: symbol,
|
||||||
ext_var,
|
ext_var,
|
||||||
|
@ -831,7 +829,6 @@ pub fn canonicalize_expr<'a>(
|
||||||
(
|
(
|
||||||
ZeroArgumentTag {
|
ZeroArgumentTag {
|
||||||
name: TagName::Private(symbol),
|
name: TagName::Private(symbol),
|
||||||
arguments: vec![],
|
|
||||||
variant_var,
|
variant_var,
|
||||||
ext_var,
|
ext_var,
|
||||||
closure_name: lambda_set_symbol,
|
closure_name: lambda_set_symbol,
|
||||||
|
@ -1560,15 +1557,13 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
variant_var,
|
variant_var,
|
||||||
ext_var,
|
ext_var,
|
||||||
name,
|
name,
|
||||||
arguments,
|
|
||||||
} => {
|
} => {
|
||||||
todo!(
|
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,
|
closure_name,
|
||||||
variant_var,
|
variant_var,
|
||||||
ext_var,
|
ext_var,
|
||||||
name,
|
name,
|
||||||
arguments
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![warn(clippy::dbg_macro)]
|
#![warn(clippy::dbg_macro)]
|
||||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||||
#![allow(clippy::large_enum_variant)]
|
#![allow(clippy::large_enum_variant)]
|
||||||
|
pub mod abilities;
|
||||||
pub mod annotation;
|
pub mod annotation;
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
pub mod constraint;
|
pub mod constraint;
|
||||||
|
|
|
@ -589,7 +589,8 @@ fn fix_values_captured_in_closure_pattern(
|
||||||
| Shadowed(..)
|
| Shadowed(..)
|
||||||
| MalformedPattern(_, _)
|
| MalformedPattern(_, _)
|
||||||
| UnsupportedPattern(_)
|
| UnsupportedPattern(_)
|
||||||
| OpaqueNotInScope(..) => (),
|
| OpaqueNotInScope(..)
|
||||||
|
| AbilityMemberSpecialization { .. } => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,6 +647,7 @@ fn fix_values_captured_in_closure_expr(
|
||||||
| Var(_)
|
| Var(_)
|
||||||
| EmptyRecord
|
| EmptyRecord
|
||||||
| RuntimeError(_)
|
| RuntimeError(_)
|
||||||
|
| ZeroArgumentTag { .. }
|
||||||
| Accessor { .. } => {}
|
| Accessor { .. } => {}
|
||||||
|
|
||||||
List { loc_elems, .. } => {
|
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);
|
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() {
|
for (_, loc_arg) in arguments.iter_mut() {
|
||||||
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::abilities::AbilitiesStore;
|
||||||
use crate::annotation::freshen_opaque_def;
|
use crate::annotation::freshen_opaque_def;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output};
|
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_module::symbol::Symbol;
|
||||||
use roc_parse::ast::{self, StrLiteral, StrSegment};
|
use roc_parse::ast::{self, StrLiteral, StrSegment};
|
||||||
use roc_parse::pattern::PatternType;
|
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_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{LambdaSet, Type};
|
use roc_types::types::{LambdaSet, Type};
|
||||||
|
@ -62,6 +63,17 @@ pub enum Pattern {
|
||||||
SingleQuote(char),
|
SingleQuote(char),
|
||||||
Underscore,
|
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
|
// Runtime Exceptions
|
||||||
Shadowed(Region, Loc<Ident>, Symbol),
|
Shadowed(Region, Loc<Ident>, Symbol),
|
||||||
OpaqueNotInScope(Loc<Ident>),
|
OpaqueNotInScope(Loc<Ident>),
|
||||||
|
@ -101,6 +113,11 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
||||||
symbols.push(*symbol);
|
symbols.push(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbilityMemberSpecialization { ident, specializes } => {
|
||||||
|
symbols.push(*ident);
|
||||||
|
symbols.push(*specializes);
|
||||||
|
}
|
||||||
|
|
||||||
AppliedTag { arguments, .. } => {
|
AppliedTag { arguments, .. } => {
|
||||||
for (_, nested) in arguments {
|
for (_, nested) in arguments {
|
||||||
symbols_from_pattern_help(&nested.value, symbols);
|
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>(
|
pub fn canonicalize_pattern<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
@ -164,6 +231,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
output.references.bound_symbols.insert(new_symbol);
|
output.references.bound_symbols.insert(new_symbol);
|
||||||
|
|
||||||
|
@ -412,6 +480,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// No matter what the other patterns
|
// No matter what the other patterns
|
||||||
|
@ -484,6 +553,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// No matter what the other patterns
|
// No matter what the other patterns
|
||||||
|
@ -594,7 +664,12 @@ fn add_bindings_from_patterns(
|
||||||
use Pattern::*;
|
use Pattern::*;
|
||||||
|
|
||||||
match pattern {
|
match pattern {
|
||||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
Identifier(symbol)
|
||||||
|
| Shadowed(_, _, symbol)
|
||||||
|
| AbilityMemberSpecialization {
|
||||||
|
ident: symbol,
|
||||||
|
specializes: _,
|
||||||
|
} => {
|
||||||
answer.push((*symbol, *region));
|
answer.push((*symbol, *region));
|
||||||
}
|
}
|
||||||
AppliedTag {
|
AppliedTag {
|
||||||
|
|
|
@ -6,6 +6,8 @@ use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{Alias, AliasKind, Type};
|
use roc_types::types::{Alias, AliasKind, Type};
|
||||||
|
|
||||||
|
use crate::abilities::AbilitiesStore;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
/// All the identifiers in scope, mapped to were they were defined and
|
/// 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
|
/// The type aliases currently in scope
|
||||||
pub aliases: SendMap<Symbol, Alias>,
|
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
|
/// The current module being processed. This will be used to turn
|
||||||
/// unqualified idents into Symbols.
|
/// unqualified idents into Symbols.
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
|
@ -62,6 +67,8 @@ impl Scope {
|
||||||
idents: Symbol::default_in_scope(),
|
idents: Symbol::default_in_scope(),
|
||||||
symbols: SendMap::default(),
|
symbols: SendMap::default(),
|
||||||
aliases,
|
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
|
/// 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.
|
/// 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(
|
pub fn introduce(
|
||||||
&mut self,
|
&mut self,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
|
@ -198,25 +210,98 @@ impl Scope {
|
||||||
|
|
||||||
Err((original_region, shadow, symbol))
|
Err((original_region, shadow, symbol))
|
||||||
}
|
}
|
||||||
None => {
|
None => Ok(self.commit_introduction(ident, exposed_ident_ids, all_ident_ids, region)),
|
||||||
// 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.
|
|
||||||
let ident_id = match exposed_ident_ids.get_id(&ident) {
|
/// Like [Self::introduce], but does not introduce a new symbol for the shadowing symbol.
|
||||||
Some(ident_id) => ident_id,
|
pub fn introduce_without_shadow_symbol(
|
||||||
None => all_ident_ids.add(ident.clone()),
|
&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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let symbol = Symbol::new(self.home, ident_id);
|
/// 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(symbol, region);
|
self.symbols.insert(shadow_symbol, region);
|
||||||
self.idents.insert(ident, (symbol, region));
|
|
||||||
|
|
||||||
Ok(symbol)
|
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.
|
||||||
|
let ident_id = match exposed_ident_ids.get_id(&ident) {
|
||||||
|
Some(ident_id) => ident_id,
|
||||||
|
None => all_ident_ids.add(ident.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let symbol = Symbol::new(self.home, ident_id);
|
||||||
|
|
||||||
|
self.symbols.insert(symbol, region);
|
||||||
|
self.idents.insert(ident, (symbol, region));
|
||||||
|
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
/// Ignore an identifier.
|
/// Ignore an identifier.
|
||||||
///
|
///
|
||||||
/// Used for record guards like { x: Just _ }
|
/// Used for record guards like { x: Just _ }
|
||||||
|
|
|
@ -881,7 +881,9 @@ pub fn constrain_expr(
|
||||||
name,
|
name,
|
||||||
arguments,
|
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 types = Vec::with_capacity(arguments.len());
|
||||||
let mut arg_cons = Vec::with_capacity(arguments.len());
|
let mut arg_cons = Vec::with_capacity(arguments.len());
|
||||||
|
|
||||||
|
@ -923,27 +925,8 @@ pub fn constrain_expr(
|
||||||
variant_var,
|
variant_var,
|
||||||
ext_var,
|
ext_var,
|
||||||
name,
|
name,
|
||||||
arguments,
|
|
||||||
closure_name,
|
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(
|
let union_con = constraints.equal_types_with_storage(
|
||||||
Type::FunctionOrTagUnion(
|
Type::FunctionOrTagUnion(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
|
@ -953,19 +936,14 @@ pub fn constrain_expr(
|
||||||
expected.clone(),
|
expected.clone(),
|
||||||
Category::TagApply {
|
Category::TagApply {
|
||||||
tag_name: name.clone(),
|
tag_name: name.clone(),
|
||||||
args_count: arguments.len(),
|
args_count: 0,
|
||||||
},
|
},
|
||||||
region,
|
region,
|
||||||
*variant_var,
|
*variant_var,
|
||||||
);
|
);
|
||||||
|
|
||||||
vars.push(*variant_var);
|
constraints.exists_many([*variant_var, *ext_var], [union_con])
|
||||||
vars.push(*ext_var);
|
|
||||||
arg_cons.push(union_con);
|
|
||||||
|
|
||||||
constraints.exists_many(vars, arg_cons)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpaqueRef {
|
OpaqueRef {
|
||||||
opaque_var,
|
opaque_var,
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -50,7 +50,13 @@ fn headers_from_annotation_help(
|
||||||
headers: &mut SendMap<Symbol, Loc<Type>>,
|
headers: &mut SendMap<Symbol, Loc<Type>>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match pattern {
|
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());
|
let typ = Loc::at(annotation.region, annotation.value.clone());
|
||||||
headers.insert(*symbol, typ);
|
headers.insert(*symbol, typ);
|
||||||
true
|
true
|
||||||
|
@ -182,7 +188,12 @@ pub fn constrain_pattern(
|
||||||
// Erroneous patterns don't add any constraints.
|
// 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()) {
|
if could_be_a_tag_union(expected.get_type_ref()) {
|
||||||
state
|
state
|
||||||
.constraints
|
.constraints
|
||||||
|
|
|
@ -5865,6 +5865,48 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
.build_int_cast_sign_flag(arg, to, to_signed, "inc_cast")
|
.build_int_cast_sign_flag(arg, to, to_signed, "inc_cast")
|
||||||
.into()
|
.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 => {
|
Eq => {
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
|
|
|
@ -677,9 +677,15 @@ impl<'a> LowLevelCall<'a> {
|
||||||
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
|
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NumToFloatCast => {
|
||||||
|
todo!("implement toF32 and toF64");
|
||||||
|
}
|
||||||
NumToIntChecked => {
|
NumToIntChecked => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
NumToFloatChecked => {
|
||||||
|
todo!("implement toF32Checked and toF64Checked");
|
||||||
|
}
|
||||||
And => {
|
And => {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.i32_and();
|
backend.code_builder.i32_and();
|
||||||
|
|
|
@ -111,7 +111,9 @@ pub enum LowLevel {
|
||||||
NumShiftRightBy,
|
NumShiftRightBy,
|
||||||
NumShiftRightZfBy,
|
NumShiftRightZfBy,
|
||||||
NumIntCast,
|
NumIntCast,
|
||||||
|
NumToFloatCast,
|
||||||
NumToIntChecked,
|
NumToIntChecked,
|
||||||
|
NumToFloatChecked,
|
||||||
NumToStr,
|
NumToStr,
|
||||||
Eq,
|
Eq,
|
||||||
NotEq,
|
NotEq,
|
||||||
|
|
|
@ -1115,7 +1115,6 @@ define_builtins! {
|
||||||
32 STR_TO_I16: "toI16"
|
32 STR_TO_I16: "toI16"
|
||||||
33 STR_TO_U8: "toU8"
|
33 STR_TO_U8: "toU8"
|
||||||
34 STR_TO_I8: "toI8"
|
34 STR_TO_I8: "toI8"
|
||||||
|
|
||||||
}
|
}
|
||||||
4 LIST: "List" => {
|
4 LIST: "List" => {
|
||||||
0 LIST_LIST: "List" imported // the List.List type alias
|
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
|
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
|
||||||
| NumRound | NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos
|
| 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]),
|
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
|
|
|
@ -13,7 +13,7 @@ use roc_exhaustive::{Ctor, Guard, RenderAs, TagId};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
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_region::all::{Loc, Region};
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
|
@ -2037,6 +2037,7 @@ fn pattern_to_when<'a>(
|
||||||
let error = roc_problem::can::RuntimeError::Shadowing {
|
let error = roc_problem::can::RuntimeError::Shadowing {
|
||||||
original_region: *region,
|
original_region: *region,
|
||||||
shadow: loc_ident.clone(),
|
shadow: loc_ident.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
};
|
};
|
||||||
(*new_symbol, Loc::at_zero(RuntimeError(error)))
|
(*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
|
// They should have been replaced with `UnsupportedPattern` during canonicalization
|
||||||
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
|
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);
|
let opt_fn_var = Some(variable);
|
||||||
|
|
||||||
// if this is a function symbol, ensure that it's properly specialized!
|
// if this is a function symbol, ensure that it's properly specialized!
|
||||||
reuse_function_symbol(
|
specialize_symbol(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|
@ -3432,7 +3440,6 @@ pub fn with_hole<'a>(
|
||||||
ZeroArgumentTag {
|
ZeroArgumentTag {
|
||||||
variant_var,
|
variant_var,
|
||||||
name: tag_name,
|
name: tag_name,
|
||||||
arguments: args,
|
|
||||||
ext_var,
|
ext_var,
|
||||||
closure_name,
|
closure_name,
|
||||||
} => {
|
} => {
|
||||||
|
@ -3466,7 +3473,7 @@ pub fn with_hole<'a>(
|
||||||
tag_name,
|
tag_name,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
args,
|
std::vec::Vec::new(),
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3558,7 +3565,7 @@ pub fn with_hole<'a>(
|
||||||
// this symbol is already defined; nothing to do
|
// this symbol is already defined; nothing to do
|
||||||
}
|
}
|
||||||
Field::Function(symbol, variable) => {
|
Field::Function(symbol, variable) => {
|
||||||
stmt = reuse_function_symbol(
|
stmt = specialize_symbol(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|
@ -4114,7 +4121,7 @@ pub fn with_hole<'a>(
|
||||||
Stmt::Let(*symbol, access_expr, *field_layout, arena.alloc(stmt));
|
Stmt::Let(*symbol, access_expr, *field_layout, arena.alloc(stmt));
|
||||||
|
|
||||||
if record_needs_specialization {
|
if record_needs_specialization {
|
||||||
stmt = reuse_function_symbol(
|
stmt = specialize_symbol(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|
@ -4804,8 +4811,7 @@ fn construct_closure_data<'a>(
|
||||||
// symbols to be inlined when specializing the closure body elsewhere.
|
// symbols to be inlined when specializing the closure body elsewhere.
|
||||||
for &&(symbol, var) in symbols {
|
for &&(symbol, var) in symbols {
|
||||||
if procs.partial_exprs.contains(symbol) {
|
if procs.partial_exprs.contains(symbol) {
|
||||||
result =
|
result = specialize_symbol(env, procs, layout_cache, Some(var), symbol, result, symbol);
|
||||||
reuse_function_symbol(env, procs, layout_cache, Some(var), symbol, result, symbol);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6318,6 +6324,20 @@ fn store_pattern_help<'a>(
|
||||||
|
|
||||||
match can_pat {
|
match can_pat {
|
||||||
Identifier(symbol) => {
|
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);
|
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
||||||
}
|
}
|
||||||
Underscore => {
|
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)
|
Stmt::Let(assigned, Expr::Struct(&[]), Layout::UNIT, hole)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the symbol is a function, make sure it is properly specialized
|
/// If the symbol is a function or polymorphic value, make sure it is properly specialized
|
||||||
// TODO: rename this now that we handle polymorphic non-function expressions too
|
fn specialize_symbol<'a>(
|
||||||
fn reuse_function_symbol<'a>(
|
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
@ -6980,7 +6999,7 @@ fn assign_to_symbol<'a>(
|
||||||
match can_reuse_symbol(env, procs, &loc_arg.value) {
|
match can_reuse_symbol(env, procs, &loc_arg.value) {
|
||||||
Imported(original) | LocalFunction(original) | UnspecializedExpr(original) => {
|
Imported(original) | LocalFunction(original) | UnspecializedExpr(original) => {
|
||||||
// for functions we must make sure they are specialized correctly
|
// for functions we must make sure they are specialized correctly
|
||||||
reuse_function_symbol(
|
specialize_symbol(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|
@ -7787,6 +7806,7 @@ fn from_can_pattern_help<'a>(
|
||||||
match can_pattern {
|
match can_pattern {
|
||||||
Underscore => Ok(Pattern::Underscore),
|
Underscore => Ok(Pattern::Underscore),
|
||||||
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
||||||
|
AbilityMemberSpecialization { ident, .. } => Ok(Pattern::Identifier(*ident)),
|
||||||
IntLiteral(_, precision_var, _, int, _bound) => {
|
IntLiteral(_, precision_var, _, int, _bound) => {
|
||||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision_var, false) {
|
match num_argument_to_int_or_float(env.subs, env.target_info, *precision_var, false) {
|
||||||
IntOrFloat::Int(precision) => {
|
IntOrFloat::Int(precision) => {
|
||||||
|
@ -7830,6 +7850,7 @@ fn from_can_pattern_help<'a>(
|
||||||
Shadowed(region, ident, _new_symbol) => Err(RuntimeError::Shadowing {
|
Shadowed(region, ident, _new_symbol) => Err(RuntimeError::Shadowing {
|
||||||
original_region: *region,
|
original_region: *region,
|
||||||
shadow: ident.clone(),
|
shadow: ident.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
}),
|
}),
|
||||||
UnsupportedPattern(region) => Err(RuntimeError::UnsupportedPattern(*region)),
|
UnsupportedPattern(region) => Err(RuntimeError::UnsupportedPattern(*region)),
|
||||||
MalformedPattern(_problem, region) => {
|
MalformedPattern(_problem, region) => {
|
||||||
|
|
|
@ -468,7 +468,7 @@ impl<'a> UnionLayout<'a> {
|
||||||
|
|
||||||
pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
||||||
let allocation = match self {
|
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::Recursive(tags) => Self::tags_alignment_bytes(tags, target_info),
|
||||||
UnionLayout::NonNullableUnwrapped(field_layouts) => {
|
UnionLayout::NonNullableUnwrapped(field_layouts) => {
|
||||||
Layout::struct_no_name_order(field_layouts).alignment_bytes(target_info)
|
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 {
|
pub fn allocation_alignment_bytes(&self, target_info: TargetInfo) -> u32 {
|
||||||
|
let ptr_width = target_info.ptr_width() as u32;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Layout::Builtin(builtin) => builtin.allocation_alignment_bytes(target_info),
|
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::Union(union_layout) => union_layout.allocation_alignment_bytes(target_info),
|
||||||
Layout::LambdaSet(lambda_set) => lambda_set
|
Layout::LambdaSet(lambda_set) => lambda_set
|
||||||
.runtime_representation()
|
.runtime_representation()
|
||||||
|
@ -1545,9 +1547,6 @@ impl<'a> Builtin<'a> {
|
||||||
let ptr_width = target_info.ptr_width() as u32;
|
let ptr_width = target_info.ptr_width() as u32;
|
||||||
|
|
||||||
let allocation = match self {
|
let allocation = match self {
|
||||||
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => {
|
|
||||||
unreachable!("not heap-allocated")
|
|
||||||
}
|
|
||||||
Builtin::Str => ptr_width,
|
Builtin::Str => ptr_width,
|
||||||
Builtin::Dict(k, v) => k
|
Builtin::Dict(k, v) => k
|
||||||
.alignment_bytes(target_info)
|
.alignment_bytes(target_info)
|
||||||
|
@ -1555,6 +1554,11 @@ impl<'a> Builtin<'a> {
|
||||||
.max(ptr_width),
|
.max(ptr_width),
|
||||||
Builtin::Set(k) => k.alignment_bytes(target_info).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),
|
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)
|
allocation.max(ptr_width)
|
||||||
|
|
|
@ -170,6 +170,7 @@ enum FirstOrder {
|
||||||
NumBytesToU32,
|
NumBytesToU32,
|
||||||
NumShiftRightZfBy,
|
NumShiftRightZfBy,
|
||||||
NumIntCast,
|
NumIntCast,
|
||||||
|
NumFloatCast,
|
||||||
Eq,
|
Eq,
|
||||||
NotEq,
|
NotEq,
|
||||||
And,
|
And,
|
||||||
|
|
|
@ -866,24 +866,26 @@ fn parse_defs_end<'a>(
|
||||||
}
|
}
|
||||||
Ok((_, loc_pattern, state)) => {
|
Ok((_, loc_pattern, state)) => {
|
||||||
// First let's check whether this is an ability definition.
|
// First let's check whether this is an ability definition.
|
||||||
if let Loc {
|
let opt_tag_and_args: Option<(&str, Region, &[Loc<Pattern>])> = match loc_pattern.value
|
||||||
value:
|
|
||||||
Pattern::Apply(
|
|
||||||
loc_name @ Loc {
|
|
||||||
value: Pattern::GlobalTag(name),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
args,
|
|
||||||
),
|
|
||||||
..
|
|
||||||
} = loc_pattern
|
|
||||||
{
|
{
|
||||||
|
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)) =
|
if let Ok((_, loc_has, state)) =
|
||||||
loc_has_parser(min_indent).parse(arena, state.clone())
|
loc_has_parser(min_indent).parse(arena, state.clone())
|
||||||
{
|
{
|
||||||
let (_, loc_def, state) = finish_parsing_ability_def(
|
let (_, loc_def, state) = finish_parsing_ability_def(
|
||||||
start_column,
|
start_column,
|
||||||
Loc::at(loc_name.region, name),
|
Loc::at(name_region, name),
|
||||||
args,
|
args,
|
||||||
loc_has,
|
loc_has,
|
||||||
arena,
|
arena,
|
||||||
|
|
|
@ -37,57 +37,47 @@ Defs(
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
@35-68 Type(
|
||||||
@35-71 SpaceBefore(
|
Ability {
|
||||||
Defs(
|
header: TypeHeader {
|
||||||
[
|
name: @35-38 "Ab2",
|
||||||
@35-68 Type(
|
vars: [],
|
||||||
Ability {
|
},
|
||||||
header: TypeHeader {
|
loc_has: @39-42 Has,
|
||||||
name: @35-38 "Ab2",
|
members: [
|
||||||
vars: [],
|
AbilityMember {
|
||||||
},
|
name: @43-46 "ab2",
|
||||||
loc_has: @39-42 Has,
|
typ: @54-68 Where(
|
||||||
members: [
|
@54-56 Function(
|
||||||
AbilityMember {
|
[
|
||||||
name: @43-46 "ab2",
|
@49-50 BoundVariable(
|
||||||
typ: @54-68 Where(
|
"a",
|
||||||
@54-56 Function(
|
|
||||||
[
|
|
||||||
@49-50 BoundVariable(
|
|
||||||
"a",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
@54-56 Record {
|
|
||||||
fields: [],
|
|
||||||
ext: None,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
[
|
],
|
||||||
@59-68 HasClause {
|
@54-56 Record {
|
||||||
var: @59-60 "a",
|
fields: [],
|
||||||
ability: @65-68 Apply(
|
ext: None,
|
||||||
"",
|
},
|
||||||
"Ab2",
|
),
|
||||||
[],
|
[
|
||||||
),
|
@59-68 HasClause {
|
||||||
},
|
var: @59-60 "a",
|
||||||
],
|
ability: @65-68 Apply(
|
||||||
),
|
"",
|
||||||
},
|
"Ab2",
|
||||||
],
|
[],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
|
||||||
],
|
|
||||||
@70-71 SpaceBefore(
|
|
||||||
Num(
|
|
||||||
"1",
|
|
||||||
),
|
|
||||||
[
|
|
||||||
Newline,
|
|
||||||
Newline,
|
|
||||||
],
|
],
|
||||||
),
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@70-71 SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"1",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
Newline,
|
Newline,
|
||||||
|
|
|
@ -20,6 +20,14 @@ pub enum BadPattern {
|
||||||
Unsupported(PatternType),
|
Unsupported(PatternType),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum ShadowKind {
|
||||||
|
Variable,
|
||||||
|
Alias,
|
||||||
|
Opaque,
|
||||||
|
Ability,
|
||||||
|
}
|
||||||
|
|
||||||
/// Problems that can occur in the course of canonicalization.
|
/// Problems that can occur in the course of canonicalization.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Problem {
|
pub enum Problem {
|
||||||
|
@ -33,9 +41,10 @@ pub enum Problem {
|
||||||
PrecedenceProblem(PrecedenceProblem),
|
PrecedenceProblem(PrecedenceProblem),
|
||||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
UnsupportedPattern(BadPattern, Region),
|
UnsupportedPattern(BadPattern, Region),
|
||||||
ShadowingInAnnotation {
|
Shadowing {
|
||||||
original_region: Region,
|
original_region: Region,
|
||||||
shadow: Loc<Ident>,
|
shadow: Loc<Ident>,
|
||||||
|
kind: ShadowKind,
|
||||||
},
|
},
|
||||||
CyclicAlias(Symbol, Region, Vec<Symbol>),
|
CyclicAlias(Symbol, Region, Vec<Symbol>),
|
||||||
BadRecursion(Vec<CycleEntry>),
|
BadRecursion(Vec<CycleEntry>),
|
||||||
|
@ -95,6 +104,30 @@ pub enum Problem {
|
||||||
region: Region,
|
region: Region,
|
||||||
kind: ExtensionTypeKind,
|
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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -157,6 +190,7 @@ pub enum RuntimeError {
|
||||||
Shadowing {
|
Shadowing {
|
||||||
original_region: Region,
|
original_region: Region,
|
||||||
shadow: Loc<Ident>,
|
shadow: Loc<Ident>,
|
||||||
|
kind: ShadowKind,
|
||||||
},
|
},
|
||||||
InvalidOptionalValue {
|
InvalidOptionalValue {
|
||||||
field_name: Lowercase,
|
field_name: Lowercase,
|
||||||
|
|
|
@ -5319,6 +5319,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]
|
#[test]
|
||||||
fn opaque_wrap_infer() {
|
fn opaque_wrap_infer() {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
|
@ -5682,4 +5697,17 @@ mod solve_expr {
|
||||||
"Result I64 [ InvalidNumStr, ListWasEmpty ]*",
|
"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 }",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2248,7 +2248,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),* ])? )*))*) => {$($(
|
($($fn:expr, $typ:ty, ($($test_name:ident, $input:expr, $output:expr $(, [ $($support_gen:literal),* ])? )*))*) => {$($(
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", $($(feature = $support_gen)*)?))]
|
#[cfg(any(feature = "gen-llvm", $($(feature = $support_gen)*)?))]
|
||||||
|
@ -2259,7 +2259,7 @@ macro_rules! to_int_tests {
|
||||||
)*)*}
|
)*)*}
|
||||||
}
|
}
|
||||||
|
|
||||||
to_int_tests! {
|
num_conversion_tests! {
|
||||||
"Num.toI8", i8, (
|
"Num.toI8", i8, (
|
||||||
to_i8_same_width, "15u8", 15, ["gen-wasm"]
|
to_i8_same_width, "15u8", 15, ["gen-wasm"]
|
||||||
to_i8_truncate, "115i32", 115, ["gen-wasm"]
|
to_i8_truncate, "115i32", 115, ["gen-wasm"]
|
||||||
|
@ -2320,6 +2320,36 @@ to_int_tests! {
|
||||||
to_nat_truncate, "115i128", 115
|
to_nat_truncate, "115i128", 115
|
||||||
to_nat_truncate_wraps, "10_000_000_000_000_000_000_000i128", 1864712049423024128
|
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 {
|
macro_rules! to_int_checked_tests {
|
||||||
|
|
|
@ -3275,3 +3275,47 @@ fn box_and_unbox_string() {
|
||||||
RocStr
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1587,3 +1587,19 @@ fn str_to_dec() {
|
||||||
RocDec
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
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;
|
|
@ -1282,6 +1282,17 @@ fn issue_2583_specialize_errors_behind_unified_branches() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn issue_2811() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x = Command { tool: "bash" }
|
||||||
|
Command c = x
|
||||||
|
c.tool
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// #[ignore]
|
// #[ignore]
|
||||||
// #[mono_test]
|
// #[mono_test]
|
||||||
// fn static_str_closure() {
|
// fn static_str_closure() {
|
||||||
|
|
|
@ -1856,6 +1856,7 @@ pub enum Problem {
|
||||||
},
|
},
|
||||||
InvalidModule,
|
InvalidModule,
|
||||||
SolvedTypeError,
|
SolvedTypeError,
|
||||||
|
HasClauseIsNotAbility(Region),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
@ -2307,15 +2308,15 @@ static THE_LETTER_A: u32 = 'a' as u32;
|
||||||
pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lowercase, u32) {
|
pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lowercase, u32) {
|
||||||
// TODO we should arena-allocate this String,
|
// TODO we should arena-allocate this String,
|
||||||
// so all the strings in the entire pass only require ~1 allocation.
|
// so all the strings in the entire pass only require ~1 allocation.
|
||||||
let generated_name = if letters_used < 26 {
|
let mut generated_name = String::with_capacity((letters_used as usize) / 26 + 1);
|
||||||
// This should generate "a", then "b", etc.
|
|
||||||
std::char::from_u32(THE_LETTER_A + letters_used)
|
let mut remaining = letters_used as i32;
|
||||||
.unwrap_or_else(|| panic!("Tried to convert {} to a char", THE_LETTER_A + letters_used))
|
while remaining >= 0 {
|
||||||
.to_string()
|
generated_name.push(std::char::from_u32(THE_LETTER_A + ((remaining as u32) % 26)).unwrap());
|
||||||
.into()
|
remaining -= 26;
|
||||||
} else {
|
}
|
||||||
panic!("TODO generate aa, ab, ac, ...");
|
|
||||||
};
|
let generated_name = generated_name.into();
|
||||||
|
|
||||||
if taken.contains(&generated_name) {
|
if taken.contains(&generated_name) {
|
||||||
// If the generated name is already taken, try again.
|
// If the generated name is already taken, try again.
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
The Roc programming language is named after [a mythical bird](https://en.wikipedia.org/wiki/Roc_(mythology)).
|
The Roc programming language is named after [a mythical bird](https://en.wikipedia.org/wiki/Roc_(mythology)).
|
||||||
|
|
||||||
That’s why the logo is a bird. It’s specifically an [*origami* bird](https://youtu.be/9gni1t1k1uY) as a homage
|
That’s why the logo is a bird. It’s specifically an [*origami* bird](https://youtu.be/9gni1t1k1uY) as an homage
|
||||||
to [Elm](https://elm-lang.org/)’s tangram logo.
|
to [Elm](https://elm-lang.org/)’s tangram logo.
|
||||||
|
|
||||||
Roc is a direct descendant of Elm. The languages are similar, but not the same.
|
Roc is a direct descendant of Elm. The languages are similar, but not the same.
|
||||||
|
|
|
@ -2,7 +2,7 @@ use roc_collections::all::MutSet;
|
||||||
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||||
use roc_problem::can::{
|
use roc_problem::can::{
|
||||||
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError,
|
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError, ShadowKind,
|
||||||
};
|
};
|
||||||
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
|
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
|
||||||
use roc_types::types::AliasKind;
|
use roc_types::types::AliasKind;
|
||||||
|
@ -38,6 +38,12 @@ const OPAQUE_DECLARED_OUTSIDE_SCOPE: &str = "OPAQUE TYPE DECLARED OUTSIDE SCOPE"
|
||||||
const OPAQUE_NOT_APPLIED: &str = "OPAQUE TYPE NOT APPLIED";
|
const OPAQUE_NOT_APPLIED: &str = "OPAQUE TYPE NOT APPLIED";
|
||||||
const OPAQUE_OVER_APPLIED: &str = "OPAQUE TYPE APPLIED TO TOO MANY ARGS";
|
const OPAQUE_OVER_APPLIED: &str = "OPAQUE TYPE APPLIED TO TOO MANY ARGS";
|
||||||
const INVALID_EXTENSION_TYPE: &str = "INVALID_EXTENSION_TYPE";
|
const INVALID_EXTENSION_TYPE: &str = "INVALID_EXTENSION_TYPE";
|
||||||
|
const ABILITY_HAS_TYPE_VARIABLES: &str = "ABILITY HAS TYPE VARIABLES";
|
||||||
|
const HAS_CLAUSE_IS_NOT_AN_ABILITY: &str = "HAS CLAUSE IS NOT AN ABILITY";
|
||||||
|
const ALIAS_USES_ABILITY: &str = "ALIAS USES ABILITY";
|
||||||
|
const ILLEGAL_HAS_CLAUSE: &str = "ILLEGAL HAS CLAUSE";
|
||||||
|
const ABILITY_MEMBER_MISSING_HAS_CLAUSE: &str = "ABILITY MEMBER MISSING HAS CLAUSE";
|
||||||
|
const ABILITY_MEMBER_HAS_EXTRANEOUS_HAS_CLAUSE: &str = "ABILITY MEMBER HAS EXTRANEOUS HAS CLAUSE";
|
||||||
|
|
||||||
pub fn can_problem<'b>(
|
pub fn can_problem<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
|
@ -213,11 +219,12 @@ pub fn can_problem<'b>(
|
||||||
title = SYNTAX_PROBLEM.to_string();
|
title = SYNTAX_PROBLEM.to_string();
|
||||||
severity = Severity::RuntimeError;
|
severity = Severity::RuntimeError;
|
||||||
}
|
}
|
||||||
Problem::ShadowingInAnnotation {
|
Problem::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow,
|
shadow,
|
||||||
|
kind,
|
||||||
} => {
|
} => {
|
||||||
doc = report_shadowing(alloc, lines, original_region, shadow);
|
doc = report_shadowing(alloc, lines, original_region, shadow, kind);
|
||||||
|
|
||||||
title = DUPLICATE_NAME.to_string();
|
title = DUPLICATE_NAME.to_string();
|
||||||
severity = Severity::RuntimeError;
|
severity = Severity::RuntimeError;
|
||||||
|
@ -562,6 +569,144 @@ pub fn can_problem<'b>(
|
||||||
title = INVALID_EXTENSION_TYPE.to_string();
|
title = INVALID_EXTENSION_TYPE.to_string();
|
||||||
severity = Severity::RuntimeError;
|
severity = Severity::RuntimeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Problem::AbilityHasTypeVariables {
|
||||||
|
name,
|
||||||
|
variables_region,
|
||||||
|
} => {
|
||||||
|
doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("The definition of the "),
|
||||||
|
alloc.symbol_unqualified(name),
|
||||||
|
alloc.reflow(" ability includes type variables:"),
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(variables_region)),
|
||||||
|
alloc.reflow(
|
||||||
|
"Abilities cannot depend on type variables, but their member values can!",
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
title = ABILITY_HAS_TYPE_VARIABLES.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Problem::HasClauseIsNotAbility {
|
||||||
|
region: clause_region,
|
||||||
|
} => {
|
||||||
|
doc = alloc.stack(vec![
|
||||||
|
alloc.reflow(r#"The type referenced in this "has" clause is not an ability:"#),
|
||||||
|
alloc.region(lines.convert_region(clause_region)),
|
||||||
|
]);
|
||||||
|
title = HAS_CLAUSE_IS_NOT_AN_ABILITY.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Problem::AliasUsesAbility {
|
||||||
|
loc_name: Loc {
|
||||||
|
region,
|
||||||
|
value: name,
|
||||||
|
},
|
||||||
|
ability,
|
||||||
|
} => {
|
||||||
|
doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("The definition of the "),
|
||||||
|
alloc.symbol_unqualified(name),
|
||||||
|
alloc.reflow(" aliases references the ability "),
|
||||||
|
alloc.symbol_unqualified(ability),
|
||||||
|
alloc.reflow(":"),
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("Abilities are not types, but you can add an ability constraint to a type variable "),
|
||||||
|
alloc.type_variable("a".into()),
|
||||||
|
alloc.reflow(" by writing"),
|
||||||
|
]),
|
||||||
|
alloc.type_block(alloc.concat(vec![
|
||||||
|
alloc.reflow("| a has "),
|
||||||
|
alloc.symbol_unqualified(ability),
|
||||||
|
])),
|
||||||
|
alloc.reflow(" at the end of the type."),
|
||||||
|
]);
|
||||||
|
title = ALIAS_USES_ABILITY.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Problem::IllegalHasClause { region } => {
|
||||||
|
doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("A "),
|
||||||
|
alloc.keyword("has"),
|
||||||
|
alloc.reflow(" clause is not allowed here:"),
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.keyword("has"),
|
||||||
|
alloc.reflow(" clauses can only be specified on the top-level type annotation of an ability member."),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
title = ILLEGAL_HAS_CLAUSE.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Problem::AbilityMemberMissingHasClause {
|
||||||
|
member,
|
||||||
|
ability,
|
||||||
|
region,
|
||||||
|
} => {
|
||||||
|
doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("The definition of the ability member "),
|
||||||
|
alloc.symbol_unqualified(member),
|
||||||
|
alloc.reflow(" does not include a "),
|
||||||
|
alloc.keyword("has"),
|
||||||
|
alloc.reflow(" clause binding a type variable to the ability "),
|
||||||
|
alloc.symbol_unqualified(ability),
|
||||||
|
alloc.reflow(":"),
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("Ability members must include a "),
|
||||||
|
alloc.keyword("has"),
|
||||||
|
alloc.reflow(" clause binding a type variable to an ability, like"),
|
||||||
|
]),
|
||||||
|
alloc.type_block(alloc.concat(vec![
|
||||||
|
alloc.type_variable("a".into()),
|
||||||
|
alloc.space(),
|
||||||
|
alloc.keyword("has"),
|
||||||
|
alloc.space(),
|
||||||
|
alloc.symbol_unqualified(ability),
|
||||||
|
])),
|
||||||
|
alloc.concat(vec![alloc.reflow(
|
||||||
|
"Otherwise, the function does not need to be part of the ability!",
|
||||||
|
)]),
|
||||||
|
]);
|
||||||
|
title = ABILITY_MEMBER_MISSING_HAS_CLAUSE.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Problem::AbilityMemberBindsExternalAbility {
|
||||||
|
member,
|
||||||
|
ability,
|
||||||
|
region,
|
||||||
|
} => {
|
||||||
|
doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("The definition of the ability member "),
|
||||||
|
alloc.symbol_unqualified(member),
|
||||||
|
alloc.reflow(" includes a has clause binding an ability it is not a part of:"),
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.reflow("Currently, ability members can only bind variables to the ability they are a part of."),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.hint(""),
|
||||||
|
alloc.reflow("Did you mean to bind the "),
|
||||||
|
alloc.symbol_unqualified(ability),
|
||||||
|
alloc.reflow(" ability instead?"),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
title = ABILITY_MEMBER_HAS_EXTRANEOUS_HAS_CLAUSE.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
|
@ -940,8 +1085,14 @@ fn report_shadowing<'b>(
|
||||||
lines: &LineInfo,
|
lines: &LineInfo,
|
||||||
original_region: Region,
|
original_region: Region,
|
||||||
shadow: Loc<Ident>,
|
shadow: Loc<Ident>,
|
||||||
|
kind: ShadowKind,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
let line = r#"Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."#;
|
let what = match kind {
|
||||||
|
ShadowKind::Variable => "variables",
|
||||||
|
ShadowKind::Alias => "aliases",
|
||||||
|
ShadowKind::Opaque => "opaques",
|
||||||
|
ShadowKind::Ability => "abilities",
|
||||||
|
};
|
||||||
|
|
||||||
alloc.stack(vec![
|
alloc.stack(vec![
|
||||||
alloc
|
alloc
|
||||||
|
@ -951,7 +1102,11 @@ fn report_shadowing<'b>(
|
||||||
alloc.region(lines.convert_region(original_region)),
|
alloc.region(lines.convert_region(original_region)),
|
||||||
alloc.reflow("But then it's defined a second time here:"),
|
alloc.reflow("But then it's defined a second time here:"),
|
||||||
alloc.region(lines.convert_region(shadow.region)),
|
alloc.region(lines.convert_region(shadow.region)),
|
||||||
alloc.reflow(line),
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("Since these "),
|
||||||
|
alloc.reflow(what),
|
||||||
|
alloc.reflow(" have the same name, it's easy to use the wrong one on accident. Give one of them a new name."),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -978,8 +1133,9 @@ fn pretty_runtime_error<'b>(
|
||||||
RuntimeError::Shadowing {
|
RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region,
|
||||||
shadow,
|
shadow,
|
||||||
|
kind,
|
||||||
} => {
|
} => {
|
||||||
doc = report_shadowing(alloc, lines, original_region, shadow);
|
doc = report_shadowing(alloc, lines, original_region, shadow, kind);
|
||||||
title = DUPLICATE_NAME;
|
title = DUPLICATE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -430,8 +430,8 @@ mod test_reporting {
|
||||||
3│ Booly : [ Yes, No, Maybe ]
|
3│ Booly : [ Yes, No, Maybe ]
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Since these variables have the same name, it's easy to use the wrong
|
Since these aliases have the same name, it's easy to use the wrong one
|
||||||
one on accident. Give one of them a new name.
|
on accident. Give one of them a new name.
|
||||||
|
|
||||||
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@ -8896,4 +8896,340 @@ I need all branches in an `if` to have the same type!
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_type_parameter_in_ability() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Hash a b c has
|
||||||
|
hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── ABILITY HAS TYPE VARIABLES ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of the `Hash` ability includes type variables:
|
||||||
|
|
||||||
|
1│ Hash a b c has
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
Abilities cannot depend on type variables, but their member values
|
||||||
|
can!
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`Hash` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Hash a b c has
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
If you didn't intend on using `Hash` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_in_has_clause() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Hash has hash : a, b -> U64 | a has Hash, b has Bool
|
||||||
|
|
||||||
|
1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── HAS CLAUSE IS NOT AN ABILITY ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The type referenced in this "has" clause is not an ability:
|
||||||
|
|
||||||
|
1│ Hash has hash : a, b -> U64 | a has Hash, b has Bool
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`hash` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Hash has hash : a, b -> U64 | a has Hash, b has Bool
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
If you didn't intend on using `hash` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shadowed_type_variable_in_has_clause() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||||||
|
|
||||||
|
1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── DUPLICATE NAME ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The `a` name is first defined here:
|
||||||
|
|
||||||
|
1│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
But then it's defined a second time here:
|
||||||
|
|
||||||
|
1│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
Since these variables have the same name, it's easy to use the wrong
|
||||||
|
one on accident. Give one of them a new name.
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`ab1` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||||||
|
^^^
|
||||||
|
|
||||||
|
If you didn't intend on using `ab1` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_using_ability() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Ability has ab : a -> {} | a has Ability
|
||||||
|
|
||||||
|
Alias : Ability
|
||||||
|
|
||||||
|
a : Alias
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── ALIAS USES ABILITY ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of the `Alias` aliases references the ability `Ability`:
|
||||||
|
|
||||||
|
3│ Alias : Ability
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
Abilities are not types, but you can add an ability constraint to a
|
||||||
|
type variable `a` by writing
|
||||||
|
|
||||||
|
| a has Ability
|
||||||
|
|
||||||
|
at the end of the type.
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`ab` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Ability has ab : a -> {} | a has Ability
|
||||||
|
^^
|
||||||
|
|
||||||
|
If you didn't intend on using `ab` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ability_shadows_ability() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Ability has ab : a -> U64 | a has Ability
|
||||||
|
|
||||||
|
Ability has ab1 : a -> U64 | a has Ability
|
||||||
|
|
||||||
|
1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── DUPLICATE NAME ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The `Ability` name is first defined here:
|
||||||
|
|
||||||
|
1│ Ability has ab : a -> U64 | a has Ability
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
But then it's defined a second time here:
|
||||||
|
|
||||||
|
3│ Ability has ab1 : a -> U64 | a has Ability
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
Since these abilities have the same name, it's easy to use the wrong
|
||||||
|
one on accident. Give one of them a new name.
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`ab` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Ability has ab : a -> U64 | a has Ability
|
||||||
|
^^
|
||||||
|
|
||||||
|
If you didn't intend on using `ab` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ability_member_does_not_bind_ability() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Ability has ab : {} -> {}
|
||||||
|
|
||||||
|
1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of the ability member `ab` does not include a `has` clause
|
||||||
|
binding a type variable to the ability `Ability`:
|
||||||
|
|
||||||
|
1│ Ability has ab : {} -> {}
|
||||||
|
^^
|
||||||
|
|
||||||
|
Ability members must include a `has` clause binding a type variable to
|
||||||
|
an ability, like
|
||||||
|
|
||||||
|
a has Ability
|
||||||
|
|
||||||
|
Otherwise, the function does not need to be part of the ability!
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`Ability` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Ability has ab : {} -> {}
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
If you didn't intend on using `Ability` then remove it so future readers
|
||||||
|
of your code don't wonder why it is there.
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`ab` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Ability has ab : {} -> {}
|
||||||
|
^^
|
||||||
|
|
||||||
|
If you didn't intend on using `ab` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ability_member_binds_extra_ability() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Eq has eq : a, a -> Bool | a has Eq
|
||||||
|
Hash has hash : a, b -> U64 | a has Eq, b has Hash
|
||||||
|
|
||||||
|
1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── ABILITY MEMBER HAS EXTRANEOUS HAS CLAUSE ────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of the ability member `hash` includes a has clause
|
||||||
|
binding an ability it is not a part of:
|
||||||
|
|
||||||
|
2│ Hash has hash : a, b -> U64 | a has Eq, b has Hash
|
||||||
|
^^^^^^^^
|
||||||
|
|
||||||
|
Currently, ability members can only bind variables to the ability they
|
||||||
|
are a part of.
|
||||||
|
|
||||||
|
Hint: Did you mean to bind the `Hash` ability instead?
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`eq` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Eq has eq : a, a -> Bool | a has Eq
|
||||||
|
^^
|
||||||
|
|
||||||
|
If you didn't intend on using `eq` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`hash` is not used anywhere in your code.
|
||||||
|
|
||||||
|
2│ Hash has hash : a, b -> U64 | a has Eq, b has Hash
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
If you didn't intend on using `hash` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn has_clause_outside_of_ability() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Hash has hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
f : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── ILLEGAL HAS CLAUSE ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
A `has` clause is not allowed here:
|
||||||
|
|
||||||
|
3│ f : a -> U64 | a has Hash
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
`has` clauses can only be specified on the top-level type annotation of
|
||||||
|
an ability member.
|
||||||
|
|
||||||
|
── UNUSED DEFINITION ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
`hash` is not used anywhere in your code.
|
||||||
|
|
||||||
|
1│ Hash has hash : a -> U64 | a has Hash
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
If you didn't intend on using `hash` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue