Merge remote-tracking branch 'origin/trunk' into solve-fully-tail-recursive

This commit is contained in:
Folkert 2022-03-05 18:34:09 +01:00
commit fc4212310f
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
29 changed files with 1514 additions and 942 deletions

View file

@ -65,3 +65,6 @@ Mats Sigge <<mats.sigge@gmail.com>>
Drew Lazzeri <dlazzeri1@gmail.com> Drew Lazzeri <dlazzeri1@gmail.com>
Tom Dohrmann <erbse.13@gmx.de> Tom Dohrmann <erbse.13@gmx.de>
Elijah Schow <elijah.schow@gmail.com> Elijah Schow <elijah.schow@gmail.com>
Derek Gustafson <degustaf@gmail.com>
Philippe Vinchon <p.vinchon@gmail.com>
Pierre-Henri Trivier <phtrivier@yahoo.fr>

2
Cargo.lock generated
View file

@ -3357,6 +3357,7 @@ dependencies = [
"roc_problem", "roc_problem",
"roc_region", "roc_region",
"roc_types", "roc_types",
"static_assertions",
"ven_graph", "ven_graph",
] ]
@ -3426,6 +3427,7 @@ dependencies = [
name = "roc_constrain" name = "roc_constrain"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"arrayvec 0.7.2",
"roc_builtins", "roc_builtins",
"roc_can", "roc_can",
"roc_collections", "roc_collections",

View file

@ -93,7 +93,7 @@ test-rust:
RUN --mount=type=cache,target=$SCCACHE_DIR \ RUN --mount=type=cache,target=$SCCACHE_DIR \
repl_test/test_wasm.sh && sccache --show-stats repl_test/test_wasm.sh && sccache --show-stats
# run i386 (32-bit linux) cli tests # run i386 (32-bit linux) cli tests
RUN echo "4" | cargo run --locked --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc RUN echo "4" | cargo run --locked --release --features="target-x86" -- --target=x86_32 examples/benchmarks/NQueens.roc
RUN --mount=type=cache,target=$SCCACHE_DIR \ RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo test --locked --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats cargo test --locked --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats

View file

@ -1,7 +1,7 @@
use bumpalo::{collections::Vec as BumpVec, Bump}; use bumpalo::{collections::Vec as BumpVec, Bump};
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{BumpMap, BumpMapDefault, Index, SendMap}; use roc_collections::all::{BumpMap, BumpMapDefault, HumanIndex, SendMap};
use roc_module::{ use roc_module::{
ident::{Lowercase, TagName}, ident::{Lowercase, TagName},
symbol::Symbol, symbol::Symbol,
@ -163,7 +163,7 @@ pub fn constrain_expr<'a>(
let elem_expected = Expected::ForReason( let elem_expected = Expected::ForReason(
Reason::ElemInList { Reason::ElemInList {
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
}, },
list_elem_type.shallow_clone(), list_elem_type.shallow_clone(),
region, region,
@ -339,7 +339,7 @@ pub fn constrain_expr<'a>(
let reason = Reason::FnArg { let reason = Reason::FnArg {
name: opt_symbol, name: opt_symbol,
arg_index: Index::zero_based(index), arg_index: HumanIndex::zero_based(index),
}; };
let expected_arg = Expected::ForReason(reason, arg_type.shallow_clone(), region); let expected_arg = Expected::ForReason(reason, arg_type.shallow_clone(), region);
@ -538,7 +538,7 @@ pub fn constrain_expr<'a>(
name.clone(), name.clone(),
arity, arity,
AnnotationSource::TypedIfBranch { AnnotationSource::TypedIfBranch {
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
num_branches, num_branches,
region: ann_source.region(), region: ann_source.region(),
}, },
@ -559,7 +559,7 @@ pub fn constrain_expr<'a>(
name, name,
arity, arity,
AnnotationSource::TypedIfBranch { AnnotationSource::TypedIfBranch {
index: Index::zero_based(branches.len()), index: HumanIndex::zero_based(branches.len()),
num_branches, num_branches,
region: ann_source.region(), region: ann_source.region(),
}, },
@ -596,7 +596,7 @@ pub fn constrain_expr<'a>(
body, body,
Expected::ForReason( Expected::ForReason(
Reason::IfBranch { Reason::IfBranch {
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
total_branches: branches.len(), total_branches: branches.len(),
}, },
Type2::Variable(*expr_var), Type2::Variable(*expr_var),
@ -616,7 +616,7 @@ pub fn constrain_expr<'a>(
final_else_expr, final_else_expr,
Expected::ForReason( Expected::ForReason(
Reason::IfBranch { Reason::IfBranch {
index: Index::zero_based(branches.len()), index: HumanIndex::zero_based(branches.len()),
total_branches: branches.len() + 1, total_branches: branches.len() + 1,
}, },
Type2::Variable(*expr_var), Type2::Variable(*expr_var),
@ -691,7 +691,7 @@ pub fn constrain_expr<'a>(
when_branch, when_branch,
PExpected::ForReason( PExpected::ForReason(
PReason::WhenMatch { PReason::WhenMatch {
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
}, },
cond_type.shallow_clone(), cond_type.shallow_clone(),
pattern_region, pattern_region,
@ -700,7 +700,7 @@ pub fn constrain_expr<'a>(
name.clone(), name.clone(),
*arity, *arity,
AnnotationSource::TypedWhenBranch { AnnotationSource::TypedWhenBranch {
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
region: ann_source.region(), region: ann_source.region(),
}, },
typ.shallow_clone(), typ.shallow_clone(),
@ -733,14 +733,14 @@ pub fn constrain_expr<'a>(
when_branch, when_branch,
PExpected::ForReason( PExpected::ForReason(
PReason::WhenMatch { PReason::WhenMatch {
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
}, },
cond_type.shallow_clone(), cond_type.shallow_clone(),
pattern_region, pattern_region,
), ),
Expected::ForReason( Expected::ForReason(
Reason::WhenBranch { Reason::WhenBranch {
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
}, },
branch_type.shallow_clone(), branch_type.shallow_clone(),
// TODO: when_branch.value.region, // TODO: when_branch.value.region,
@ -1065,7 +1065,7 @@ pub fn constrain_expr<'a>(
let reason = Reason::LowLevelOpArg { let reason = Reason::LowLevelOpArg {
op: *op, op: *op,
arg_index: Index::zero_based(index), arg_index: HumanIndex::zero_based(index),
}; };
let expected_arg = let expected_arg =
Expected::ForReason(reason, arg_type.shallow_clone(), Region::zero()); Expected::ForReason(reason, arg_type.shallow_clone(), Region::zero());
@ -1681,7 +1681,7 @@ fn constrain_tag_pattern<'a>(
let expected = PExpected::ForReason( let expected = PExpected::ForReason(
PReason::TagArg { PReason::TagArg {
tag_name: tag_name.clone(), tag_name: tag_name.clone(),
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
}, },
pattern_type, pattern_type,
region, region,

View file

@ -1,133 +0,0 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_fmt::def::fmt_def;
use roc_fmt::module::fmt_module;
use roc_parse::ast::{Def, Module};
use roc_parse::module::module_defs;
use roc_parse::parser;
use roc_parse::parser::{Parser, SyntaxError};
use roc_region::all::Located;
use std::ffi::OsStr;
use std::path::Path;
use std::{fs, io};
#[derive(Debug)]
pub struct File<'a> {
path: &'a Path,
module_header: Module<'a>,
content: Vec<'a, Located<Def<'a>>>,
}
#[derive(Debug)]
pub enum ReadError<'a> {
Read(std::io::Error),
ParseDefs(SyntaxError<'a>),
ParseHeader(SyntaxError<'a>),
DoesntHaveRocExtension,
}
impl<'a> File<'a> {
pub fn read(path: &'a Path, arena: &'a Bump) -> Result<File<'a>, ReadError<'a>> {
if path.extension() != Some(OsStr::new("roc")) {
return Err(ReadError::DoesntHaveRocExtension);
}
let bytes = fs::read(path).map_err(ReadError::Read)?;
let allocation = arena.alloc(bytes);
let module_parse_state = parser::State::new(allocation);
let parsed_module = roc_parse::module::parse_header(arena, module_parse_state);
match parsed_module {
Ok((module, state)) => {
let parsed_defs = module_defs().parse(arena, state);
match parsed_defs {
Ok((_, defs, _)) => Ok(File {
path,
module_header: module,
content: defs,
}),
Err((_, error, _)) => Err(ReadError::ParseDefs(error)),
}
}
Err(error) => Err(ReadError::ParseHeader(SyntaxError::Header(error))),
}
}
pub fn fmt(&self) -> String {
let arena = Bump::new();
let mut formatted_file = String::new();
let mut module_header_buf = bumpalo::collections::String::new_in(&arena);
fmt_module(&mut module_header_buf, &self.module_header);
formatted_file.push_str(module_header_buf.as_str());
for def in &self.content {
let mut def_buf = bumpalo::collections::String::new_in(&arena);
fmt_def(&mut def_buf, &def.value, 0);
formatted_file.push_str(def_buf.as_str());
}
formatted_file
}
pub fn fmt_then_write_to(&self, write_path: &'a Path) -> io::Result<()> {
let formatted_file = self.fmt();
fs::write(write_path, formatted_file)
}
pub fn fmt_then_write_with_name(&self, new_name: &str) -> io::Result<()> {
self.fmt_then_write_to(
self.path
.with_file_name(new_name)
.with_extension("roc")
.as_path(),
)
}
pub fn fmt_then_write(&self) -> io::Result<()> {
self.fmt_then_write_to(self.path)
}
}
#[cfg(test)]
mod test_file {
use crate::lang::roc_file;
use bumpalo::Bump;
use std::path::Path;
#[test]
fn read_and_fmt_simple_roc_module() {
let simple_module_path = Path::new("./tests/modules/SimpleUnformatted.roc");
let arena = Bump::new();
let file = roc_file::File::read(simple_module_path, &arena)
.expect("Could not read SimpleUnformatted.roc in test_file test");
assert_eq!(
file.fmt(),
indoc!(
r#"
interface Simple
exposes [
v, x
]
imports []
v : Str
v = "Value!"
x : Int
x = 4"#
)
);
}
}

View file

@ -34,7 +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_BACKEND: &str = "backend"; 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";
pub const FLAG_PRECOMPILED: &str = "precompiled-host"; pub const FLAG_PRECOMPILED: &str = "precompiled-host";
@ -42,7 +42,6 @@ pub const FLAG_VALGRIND: &str = "valgrind";
pub const FLAG_CHECK: &str = "check"; pub const FLAG_CHECK: &str = "check";
pub const ROC_FILE: &str = "ROC_FILE"; pub const ROC_FILE: &str = "ROC_FILE";
pub const ROC_DIR: &str = "ROC_DIR"; pub const ROC_DIR: &str = "ROC_DIR";
pub const BACKEND: &str = "BACKEND";
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES"; pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP"; pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
@ -76,12 +75,11 @@ pub fn build_app<'a>() -> App<'a> {
.required(false), .required(false),
) )
.arg( .arg(
Arg::new(FLAG_BACKEND) Arg::new(FLAG_TARGET)
.long(FLAG_BACKEND) .long(FLAG_TARGET)
.about("Choose a different backend") .about("Choose a different target")
// .requires(BACKEND) .default_value(Target::default().as_str())
.default_value(Backend::default().as_str()) .possible_values(Target::OPTIONS)
.possible_values(Backend::OPTIONS)
.required(false), .required(false),
) )
.arg( .arg(
@ -212,12 +210,11 @@ pub fn build_app<'a>() -> App<'a> {
.required(false), .required(false),
) )
.arg( .arg(
Arg::new(FLAG_BACKEND) Arg::new(FLAG_TARGET)
.long(FLAG_BACKEND) .long(FLAG_TARGET)
.about("Choose a different backend") .about("Choose a different target")
// .requires(BACKEND) .default_value(Target::default().as_str())
.default_value(Backend::default().as_str()) .possible_values(Target::OPTIONS)
.possible_values(Backend::OPTIONS)
.required(false), .required(false),
) )
.arg( .arg(
@ -273,12 +270,12 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
use std::str::FromStr; use std::str::FromStr;
use BuildConfig::*; use BuildConfig::*;
let backend = match matches.value_of(FLAG_BACKEND) { let target = match matches.value_of(FLAG_TARGET) {
Some(name) => Backend::from_str(name).unwrap(), Some(name) => Target::from_str(name).unwrap(),
None => Backend::default(), None => Target::default(),
}; };
let target = backend.to_triple(); let triple = target.to_triple();
let arena = Bump::new(); let arena = Bump::new();
let filename = matches.value_of(ROC_FILE).unwrap(); let filename = matches.value_of(ROC_FILE).unwrap();
@ -306,10 +303,10 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
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);
if surgically_link && !roc_linker::supported(&link_type, &target) { if surgically_link && !roc_linker::supported(&link_type, &triple) {
panic!( panic!(
"Link type, {:?}, with target, {}, not supported by roc linker", "Link type, {:?}, with target, {}, not supported by roc linker",
link_type, target link_type, triple
); );
} }
@ -338,7 +335,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
let target_valgrind = matches.is_present(FLAG_VALGRIND); let target_valgrind = matches.is_present(FLAG_VALGRIND);
let res_binary_path = build_file( let res_binary_path = build_file(
&arena, &arena,
&target, &triple,
src_dir, src_dir,
path, path,
opt_level, opt_level,
@ -377,7 +374,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
Ok(outcome.status_code()) Ok(outcome.status_code())
} }
BuildAndRun { roc_file_arg_index } => { BuildAndRun { roc_file_arg_index } => {
let mut cmd = match target.architecture { let mut cmd = match triple.architecture {
Architecture::Wasm32 => { Architecture::Wasm32 => {
// If possible, report the generated executable name relative to the current dir. // If possible, report the generated executable name relative to the current dir.
let generated_filename = binary_path let generated_filename = binary_path
@ -398,7 +395,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
_ => Command::new(&binary_path), _ => Command::new(&binary_path),
}; };
if let Architecture::Wasm32 = target.architecture { if let Architecture::Wasm32 = triple.architecture {
cmd.arg(binary_path); cmd.arg(binary_path);
} }
@ -503,43 +500,43 @@ fn run_with_wasmer(_wasm_path: &std::path::Path, _args: &[String]) {
println!("Running wasm files not support"); println!("Running wasm files not support");
} }
enum Backend { enum Target {
Host, Host,
X86_32, X86_32,
X86_64, X86_64,
Wasm32, Wasm32,
} }
impl Default for Backend { impl Default for Target {
fn default() -> Self { fn default() -> Self {
Backend::Host Target::Host
} }
} }
impl Backend { impl Target {
const fn as_str(&self) -> &'static str { const fn as_str(&self) -> &'static str {
match self { match self {
Backend::Host => "host", Target::Host => "host",
Backend::X86_32 => "x86_32", Target::X86_32 => "x86_32",
Backend::X86_64 => "x86_64", Target::X86_64 => "x86_64",
Backend::Wasm32 => "wasm32", Target::Wasm32 => "wasm32",
} }
} }
/// NOTE keep up to date! /// NOTE keep up to date!
const OPTIONS: &'static [&'static str] = &[ const OPTIONS: &'static [&'static str] = &[
Backend::Host.as_str(), Target::Host.as_str(),
Backend::X86_32.as_str(), Target::X86_32.as_str(),
Backend::X86_64.as_str(), Target::X86_64.as_str(),
Backend::Wasm32.as_str(), Target::Wasm32.as_str(),
]; ];
fn to_triple(&self) -> Triple { fn to_triple(&self) -> Triple {
let mut triple = Triple::unknown(); let mut triple = Triple::unknown();
match self { match self {
Backend::Host => Triple::host(), Target::Host => Triple::host(),
Backend::X86_32 => { Target::X86_32 => {
triple.architecture = Architecture::X86_32(X86_32Architecture::I386); triple.architecture = Architecture::X86_32(X86_32Architecture::I386);
triple.binary_format = BinaryFormat::Elf; triple.binary_format = BinaryFormat::Elf;
@ -548,13 +545,13 @@ impl Backend {
triple triple
} }
Backend::X86_64 => { Target::X86_64 => {
triple.architecture = Architecture::X86_64; triple.architecture = Architecture::X86_64;
triple.binary_format = BinaryFormat::Elf; triple.binary_format = BinaryFormat::Elf;
triple triple
} }
Backend::Wasm32 => { Target::Wasm32 => {
triple.architecture = Architecture::Wasm32; triple.architecture = Architecture::Wasm32;
triple.binary_format = BinaryFormat::Wasm; triple.binary_format = BinaryFormat::Wasm;
@ -564,21 +561,21 @@ impl Backend {
} }
} }
impl std::fmt::Display for Backend { impl std::fmt::Display for Target {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_str()) write!(f, "{}", self.as_str())
} }
} }
impl std::str::FromStr for Backend { impl std::str::FromStr for Target {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"host" => Ok(Backend::Host), "host" => Ok(Target::Host),
"x86_32" => Ok(Backend::X86_32), "x86_32" => Ok(Target::X86_32),
"x86_64" => Ok(Backend::X86_64), "x86_64" => Ok(Target::X86_64),
"wasm32" => Ok(Backend::Wasm32), "wasm32" => Ok(Target::Wasm32),
_ => Err(()), _ => Err(()),
} }
} }

View file

@ -194,7 +194,7 @@ mod cli_run {
) { ) {
assert_eq!(input_file, None, "Wasm does not support input files"); assert_eq!(input_file, None, "Wasm does not support input files");
let mut flags = flags.to_vec(); let mut flags = flags.to_vec();
flags.push("--backend=wasm32"); flags.push("--target=wasm32");
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat()); let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
if !compile_out.stderr.is_empty() { if !compile_out.stderr.is_empty() {
@ -565,7 +565,7 @@ mod cli_run {
&file_name, &file_name,
benchmark.stdin, benchmark.stdin,
benchmark.executable_filename, benchmark.executable_filename,
&["--backend=x86_32"], &["--target=x86_32"],
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))), benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
benchmark.expected_ending, benchmark.expected_ending,
benchmark.use_valgrind, benchmark.use_valgrind,
@ -575,7 +575,7 @@ mod cli_run {
&file_name, &file_name,
benchmark.stdin, benchmark.stdin,
benchmark.executable_filename, benchmark.executable_filename,
&["--backend=x86_32", "--optimize"], &["--target=x86_32", "--optimize"],
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))), benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
benchmark.expected_ending, benchmark.expected_ending,
benchmark.use_valgrind, benchmark.use_valgrind,

View file

@ -16,6 +16,7 @@ roc_types = { path = "../types" }
roc_builtins = { path = "../builtins" } roc_builtins = { path = "../builtins" }
ven_graph = { path = "../../vendor/pathfinding" } ven_graph = { path = "../../vendor/pathfinding" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
static_assertions = "1.1.0"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"

View file

@ -1,175 +1,482 @@
use crate::expected::{Expected, PExpected}; use crate::expected::{Expected, PExpected};
use roc_collections::all::{MutSet, SendMap}; use roc_collections::soa::{Index, Slice};
use roc_module::{ident::TagName, symbol::Symbol}; use roc_module::ident::TagName;
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::subs::Variable;
use roc_types::types::{Category, PatternCategory, Type}; use roc_types::types::{Category, PatternCategory, Type};
use roc_types::{subs::Variable, types::VariableDetail};
/// A presence constraint is an additive constraint that defines the lower bound #[derive(Debug)]
/// of a type. For example, `Present(t1, IncludesTag(A, []))` means that the pub struct Constraints {
/// type `t1` must contain at least the tag `A`. The additive nature of these pub constraints: Vec<Constraint>,
/// constraints makes them behaviorally different from unification-based constraints. pub types: Vec<Type>,
#[derive(Debug, Clone, PartialEq)] pub variables: Vec<Variable>,
pub enum PresenceConstraint { pub def_types: Vec<(Symbol, Loc<Index<Type>>)>,
IncludesTag(TagName, Vec<Type>, Region, PatternCategory), pub let_constraints: Vec<LetConstraint>,
IsOpen, pub categories: Vec<Category>,
Pattern(Region, PatternCategory, PExpected<Type>), pub pattern_categories: Vec<PatternCategory>,
pub expectations: Vec<Expected<Type>>,
pub pattern_expectations: Vec<PExpected<Type>>,
pub includes_tags: Vec<IncludesTag>,
pub strings: Vec<&'static str>,
} }
impl Default for Constraints {
fn default() -> Self {
Self::new()
}
}
impl Constraints {
pub fn new() -> Self {
let constraints = Vec::new();
let mut types = Vec::new();
let variables = Vec::new();
let def_types = Vec::new();
let let_constraints = Vec::new();
let mut categories = Vec::with_capacity(16);
let mut pattern_categories = Vec::with_capacity(16);
let expectations = Vec::new();
let pattern_expectations = Vec::new();
let includes_tags = Vec::new();
let strings = Vec::new();
types.extend([Type::EmptyRec, Type::EmptyTagUnion]);
categories.extend([
Category::Record,
Category::ForeignCall,
Category::OpaqueArg,
Category::Lambda,
Category::ClosureSize,
Category::StrInterpolation,
Category::If,
Category::When,
Category::Float,
Category::Int,
Category::Num,
Category::List,
Category::Str,
Category::Character,
]);
pattern_categories.extend([
PatternCategory::Record,
PatternCategory::EmptyRecord,
PatternCategory::PatternGuard,
PatternCategory::PatternDefault,
PatternCategory::Set,
PatternCategory::Map,
PatternCategory::Str,
PatternCategory::Num,
PatternCategory::Int,
PatternCategory::Float,
PatternCategory::Character,
]);
Self {
constraints,
types,
variables,
def_types,
let_constraints,
categories,
pattern_categories,
expectations,
pattern_expectations,
includes_tags,
strings,
}
}
pub const EMPTY_RECORD: Index<Type> = Index::new(0);
pub const EMPTY_TAG_UNION: Index<Type> = Index::new(1);
pub const CATEGORY_RECORD: Index<Category> = Index::new(0);
pub const CATEGORY_FOREIGNCALL: Index<Category> = Index::new(1);
pub const CATEGORY_OPAQUEARG: Index<Category> = Index::new(2);
pub const CATEGORY_LAMBDA: Index<Category> = Index::new(3);
pub const CATEGORY_CLOSURESIZE: Index<Category> = Index::new(4);
pub const CATEGORY_STRINTERPOLATION: Index<Category> = Index::new(5);
pub const CATEGORY_IF: Index<Category> = Index::new(6);
pub const CATEGORY_WHEN: Index<Category> = Index::new(7);
pub const CATEGORY_FLOAT: Index<Category> = Index::new(8);
pub const CATEGORY_INT: Index<Category> = Index::new(9);
pub const CATEGORY_NUM: Index<Category> = Index::new(10);
pub const CATEGORY_LIST: Index<Category> = Index::new(11);
pub const CATEGORY_STR: Index<Category> = Index::new(12);
pub const CATEGORY_CHARACTER: Index<Category> = Index::new(13);
pub const PCATEGORY_RECORD: Index<PatternCategory> = Index::new(0);
pub const PCATEGORY_EMPTYRECORD: Index<PatternCategory> = Index::new(1);
pub const PCATEGORY_PATTERNGUARD: Index<PatternCategory> = Index::new(2);
pub const PCATEGORY_PATTERNDEFAULT: Index<PatternCategory> = Index::new(3);
pub const PCATEGORY_SET: Index<PatternCategory> = Index::new(4);
pub const PCATEGORY_MAP: Index<PatternCategory> = Index::new(5);
pub const PCATEGORY_STR: Index<PatternCategory> = Index::new(6);
pub const PCATEGORY_NUM: Index<PatternCategory> = Index::new(7);
pub const PCATEGORY_INT: Index<PatternCategory> = Index::new(8);
pub const PCATEGORY_FLOAT: Index<PatternCategory> = Index::new(9);
pub const PCATEGORY_CHARACTER: Index<PatternCategory> = Index::new(10);
#[inline(always)]
pub fn push_type(&mut self, typ: Type) -> Index<Type> {
match typ {
Type::EmptyRec => Self::EMPTY_RECORD,
Type::EmptyTagUnion => Self::EMPTY_TAG_UNION,
other => Index::push_new(&mut self.types, other),
}
}
#[inline(always)]
pub fn push_expected_type(&mut self, expected: Expected<Type>) -> Index<Expected<Type>> {
Index::push_new(&mut self.expectations, expected)
}
#[inline(always)]
pub fn push_category(&mut self, category: Category) -> Index<Category> {
match category {
Category::Record => Self::CATEGORY_RECORD,
Category::ForeignCall => Self::CATEGORY_FOREIGNCALL,
Category::OpaqueArg => Self::CATEGORY_OPAQUEARG,
Category::Lambda => Self::CATEGORY_LAMBDA,
Category::ClosureSize => Self::CATEGORY_CLOSURESIZE,
Category::StrInterpolation => Self::CATEGORY_STRINTERPOLATION,
Category::If => Self::CATEGORY_IF,
Category::When => Self::CATEGORY_WHEN,
Category::Float => Self::CATEGORY_FLOAT,
Category::Int => Self::CATEGORY_INT,
Category::Num => Self::CATEGORY_NUM,
Category::List => Self::CATEGORY_LIST,
Category::Str => Self::CATEGORY_STR,
Category::Character => Self::CATEGORY_CHARACTER,
other => Index::push_new(&mut self.categories, other),
}
}
#[inline(always)]
pub fn push_pattern_category(&mut self, category: PatternCategory) -> Index<PatternCategory> {
match category {
PatternCategory::Record => Self::PCATEGORY_RECORD,
PatternCategory::EmptyRecord => Self::PCATEGORY_EMPTYRECORD,
PatternCategory::PatternGuard => Self::PCATEGORY_PATTERNGUARD,
PatternCategory::PatternDefault => Self::PCATEGORY_PATTERNDEFAULT,
PatternCategory::Set => Self::PCATEGORY_SET,
PatternCategory::Map => Self::PCATEGORY_MAP,
PatternCategory::Str => Self::PCATEGORY_STR,
PatternCategory::Num => Self::PCATEGORY_NUM,
PatternCategory::Int => Self::PCATEGORY_INT,
PatternCategory::Float => Self::PCATEGORY_FLOAT,
PatternCategory::Character => Self::PCATEGORY_CHARACTER,
other => Index::push_new(&mut self.pattern_categories, other),
}
}
pub fn equal_types(
&mut self,
typ: Type,
expected: Expected<Type>,
category: Category,
region: Region,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let expected_index = Index::push_new(&mut self.expectations, expected);
let category_index = Self::push_category(self, category);
Constraint::Eq(type_index, expected_index, category_index, region)
}
pub fn equal_pattern_types(
&mut self,
typ: Type,
expected: PExpected<Type>,
category: PatternCategory,
region: Region,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
let category_index = Self::push_pattern_category(self, category);
Constraint::Pattern(type_index, expected_index, category_index, region)
}
pub fn pattern_presence(
&mut self,
typ: Type,
expected: PExpected<Type>,
category: PatternCategory,
region: Region,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
let category_index = Index::push_new(&mut self.pattern_categories, category);
Constraint::PatternPresence(type_index, expected_index, category_index, region)
}
pub fn is_open_type(&mut self, typ: Type) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
Constraint::IsOpenType(type_index)
}
pub fn includes_tag<I>(
&mut self,
typ: Type,
tag_name: TagName,
types: I,
category: PatternCategory,
region: Region,
) -> Constraint
where
I: IntoIterator<Item = Type>,
{
let type_index = Index::push_new(&mut self.types, typ);
let category_index = Index::push_new(&mut self.pattern_categories, category);
let types_slice = Slice::extend_new(&mut self.types, types);
let includes_tag = IncludesTag {
type_index,
tag_name,
types: types_slice,
pattern_category: category_index,
region,
};
let includes_tag_index = Index::push_new(&mut self.includes_tags, includes_tag);
Constraint::IncludesTag(includes_tag_index)
}
fn variable_slice<I>(&mut self, it: I) -> Slice<Variable>
where
I: IntoIterator<Item = Variable>,
{
let start = self.variables.len();
self.variables.extend(it);
let length = self.variables.len() - start;
Slice::new(start as _, length as _)
}
fn def_types_slice<I>(&mut self, it: I) -> Slice<(Symbol, Loc<Index<Type>>)>
where
I: IntoIterator<Item = (Symbol, Loc<Type>)>,
{
let start = self.def_types.len();
for (symbol, loc_type) in it {
let Loc { region, value } = loc_type;
let type_index = Index::push_new(&mut self.types, value);
self.def_types.push((symbol, Loc::at(region, type_index)));
}
let length = self.def_types.len() - start;
Slice::new(start as _, length as _)
}
pub fn exists<I>(&mut self, flex_vars: I, defs_constraint: Constraint) -> Constraint
where
I: IntoIterator<Item = Variable>,
{
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
self.constraints.push(defs_constraint);
self.constraints.push(Constraint::True);
let let_contraint = LetConstraint {
rigid_vars: Slice::default(),
flex_vars: self.variable_slice(flex_vars),
def_types: Slice::default(),
defs_and_ret_constraint,
};
let let_index = Index::new(self.let_constraints.len() as _);
self.let_constraints.push(let_contraint);
Constraint::Let(let_index)
}
pub fn exists_many<I, C>(&mut self, flex_vars: I, defs_constraint: C) -> Constraint
where
I: IntoIterator<Item = Variable>,
C: IntoIterator<Item = Constraint>,
C::IntoIter: ExactSizeIterator,
{
let defs_constraint = self.and_constraint(defs_constraint);
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
self.constraints.push(defs_constraint);
self.constraints.push(Constraint::True);
let let_contraint = LetConstraint {
rigid_vars: Slice::default(),
flex_vars: self.variable_slice(flex_vars),
def_types: Slice::default(),
defs_and_ret_constraint,
};
let let_index = Index::new(self.let_constraints.len() as _);
self.let_constraints.push(let_contraint);
Constraint::Let(let_index)
}
pub fn let_constraint<I1, I2, I3>(
&mut self,
rigid_vars: I1,
flex_vars: I2,
def_types: I3,
defs_constraint: Constraint,
ret_constraint: Constraint,
) -> Constraint
where
I1: IntoIterator<Item = Variable>,
I2: IntoIterator<Item = Variable>,
I3: IntoIterator<Item = (Symbol, Loc<Type>)>,
{
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
self.constraints.push(defs_constraint);
self.constraints.push(ret_constraint);
let let_contraint = LetConstraint {
rigid_vars: self.variable_slice(rigid_vars),
flex_vars: self.variable_slice(flex_vars),
def_types: self.def_types_slice(def_types),
defs_and_ret_constraint,
};
let let_index = Index::new(self.let_constraints.len() as _);
self.let_constraints.push(let_contraint);
Constraint::Let(let_index)
}
pub fn and_constraint<I>(&mut self, constraints: I) -> Constraint
where
I: IntoIterator<Item = Constraint>,
I::IntoIter: ExactSizeIterator,
{
let mut it = constraints.into_iter();
match it.len() {
0 => Constraint::True,
1 => it.next().unwrap(),
_ => {
let start = self.constraints.len() as u32;
self.constraints.extend(it);
let end = self.constraints.len() as u32;
let slice = Slice::new(start, (end - start) as u16);
Constraint::And(slice)
}
}
}
pub fn lookup(
&mut self,
symbol: Symbol,
expected: Expected<Type>,
region: Region,
) -> Constraint {
Constraint::Lookup(
symbol,
Index::push_new(&mut self.expectations, expected),
region,
)
}
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
match constraint {
Constraint::Eq(..) => false,
Constraint::Store(..) => false,
Constraint::Lookup(..) => false,
Constraint::Pattern(..) => false,
Constraint::True => false,
Constraint::SaveTheEnvironment => true,
Constraint::Let(index) => {
let let_constraint = &self.let_constraints[index.index()];
let offset = let_constraint.defs_and_ret_constraint.index();
let defs_constraint = &self.constraints[offset];
let ret_constraint = &self.constraints[offset + 1];
self.contains_save_the_environment(defs_constraint)
|| self.contains_save_the_environment(ret_constraint)
}
Constraint::And(slice) => {
let constraints = &self.constraints[slice.indices()];
constraints
.iter()
.any(|c| self.contains_save_the_environment(c))
}
Constraint::IsOpenType(_) => false,
Constraint::IncludesTag(_) => false,
Constraint::PatternPresence(_, _, _, _) => false,
}
}
pub fn store(
&mut self,
typ: Type,
variable: Variable,
filename: &'static str,
line_number: u32,
) -> Constraint {
let type_index = Index::push_new(&mut self.types, typ);
let string_index = Index::push_new(&mut self.strings, filename);
Constraint::Store(type_index, variable, string_index, line_number)
}
}
static_assertions::assert_eq_size!([u8; 3 * 8], Constraint);
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Constraint { pub enum Constraint {
Eq(Type, Expected<Type>, Category, Region), Eq(Index<Type>, Index<Expected<Type>>, Index<Category>, Region),
Store(Type, Variable, &'static str, u32), Store(Index<Type>, Variable, Index<&'static str>, u32),
Lookup(Symbol, Expected<Type>, Region), Lookup(Symbol, Index<Expected<Type>>, Region),
Pattern(Region, PatternCategory, Type, PExpected<Type>), Pattern(
Index<Type>,
Index<PExpected<Type>>,
Index<PatternCategory>,
Region,
),
True, // Used for things that always unify, e.g. blanks and runtime errors True, // Used for things that always unify, e.g. blanks and runtime errors
SaveTheEnvironment, SaveTheEnvironment,
Let(Box<LetConstraint>), Let(Index<LetConstraint>),
And(Vec<Constraint>), And(Slice<Constraint>),
Present(Type, PresenceConstraint), /// Presence constraints
IsOpenType(Index<Type>), // Theory; always applied to a variable? if yes the use that
IncludesTag(Index<IncludesTag>),
PatternPresence(
Index<Type>,
Index<PExpected<Type>>,
Index<PatternCategory>,
Region,
),
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct LetConstraint { pub struct LetConstraint {
pub rigid_vars: Vec<Variable>, pub rigid_vars: Slice<Variable>,
pub flex_vars: Vec<Variable>, pub flex_vars: Slice<Variable>,
pub def_types: SendMap<Symbol, Loc<Type>>, pub def_types: Slice<(Symbol, Loc<Index<Type>>)>,
pub defs_constraint: Constraint, pub defs_and_ret_constraint: Index<(Constraint, Constraint)>,
pub ret_constraint: Constraint,
} }
// VALIDATE #[derive(Debug, Clone, PartialEq)]
pub struct IncludesTag {
#[derive(Default, Clone)] pub type_index: Index<Type>,
struct Declared { pub tag_name: TagName,
pub rigid_vars: MutSet<Variable>, pub types: Slice<Type>,
pub flex_vars: MutSet<Variable>, pub pattern_category: Index<PatternCategory>,
} pub region: Region,
impl Constraint {
pub fn validate(&self) -> bool {
let mut unbound = Default::default();
validate_help(self, &Declared::default(), &mut unbound);
if !unbound.type_variables.is_empty() {
panic!("found unbound type variables {:?}", &unbound.type_variables);
}
if !unbound.lambda_set_variables.is_empty() {
panic!(
"found unbound lambda set variables {:?}",
&unbound.lambda_set_variables
);
}
if !unbound.recursion_variables.is_empty() {
panic!(
"found unbound recursion variables {:?}",
&unbound.recursion_variables
);
}
true
}
pub fn contains_save_the_environment(&self) -> bool {
match self {
Constraint::Eq(_, _, _, _) => false,
Constraint::Store(_, _, _, _) => false,
Constraint::Lookup(_, _, _) => false,
Constraint::Pattern(_, _, _, _) => false,
Constraint::True => false,
Constraint::SaveTheEnvironment => true,
Constraint::Let(boxed) => {
boxed.ret_constraint.contains_save_the_environment()
|| boxed.defs_constraint.contains_save_the_environment()
}
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
Constraint::Present(_, _) => false,
}
}
}
fn subtract(declared: &Declared, detail: &VariableDetail, accum: &mut VariableDetail) {
for var in &detail.type_variables {
if !(declared.rigid_vars.contains(var) || declared.flex_vars.contains(var)) {
accum.type_variables.insert(*var);
}
}
// lambda set variables are always flex
for var in &detail.lambda_set_variables {
if declared.rigid_vars.contains(var) {
panic!("lambda set variable {:?} is declared as rigid", var);
}
if !declared.flex_vars.contains(var) {
accum.lambda_set_variables.push(*var);
}
}
// recursion vars should be always rigid
for var in &detail.recursion_variables {
if declared.flex_vars.contains(var) {
panic!("recursion variable {:?} is declared as flex", var);
}
if !declared.rigid_vars.contains(var) {
accum.recursion_variables.insert(*var);
}
}
}
fn validate_help(constraint: &Constraint, declared: &Declared, accum: &mut VariableDetail) {
use Constraint::*;
match constraint {
True | SaveTheEnvironment | Lookup(_, _, _) => { /* nothing */ }
Store(typ, var, _, _) => {
subtract(declared, &typ.variables_detail(), accum);
if !declared.flex_vars.contains(var) {
accum.type_variables.insert(*var);
}
}
Constraint::Eq(typ, expected, _, _) => {
subtract(declared, &typ.variables_detail(), accum);
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
}
Constraint::Pattern(_, _, typ, expected) => {
subtract(declared, &typ.variables_detail(), accum);
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
}
Constraint::Let(letcon) => {
let mut declared = declared.clone();
declared
.rigid_vars
.extend(letcon.rigid_vars.iter().copied());
declared.flex_vars.extend(letcon.flex_vars.iter().copied());
validate_help(&letcon.defs_constraint, &declared, accum);
validate_help(&letcon.ret_constraint, &declared, accum);
}
Constraint::And(inner) => {
for c in inner {
validate_help(c, declared, accum);
}
}
Constraint::Present(typ, constr) => {
subtract(declared, &typ.variables_detail(), accum);
match constr {
PresenceConstraint::IncludesTag(_, tys, _, _) => {
for ty in tys {
subtract(declared, &ty.variables_detail(), accum);
}
}
PresenceConstraint::IsOpen => {}
PresenceConstraint::Pattern(_, _, expected) => {
subtract(declared, &typ.variables_detail(), accum);
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
}
}
}
}
} }

View file

@ -161,13 +161,13 @@ where
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Index(usize); pub struct HumanIndex(usize);
impl Index { impl HumanIndex {
pub const FIRST: Self = Index(0); pub const FIRST: Self = HumanIndex(0);
pub fn zero_based(i: usize) -> Self { pub fn zero_based(i: usize) -> Self {
Index(i) HumanIndex(i)
} }
pub fn to_zero_based(self) -> usize { pub fn to_zero_based(self) -> usize {
@ -175,7 +175,7 @@ impl Index {
} }
pub fn one_based(i: usize) -> Self { pub fn one_based(i: usize) -> Self {
Index(i - 1) HumanIndex(i - 1)
} }
pub fn ordinal(self) -> std::string::String { pub fn ordinal(self) -> std::string::String {

View file

@ -3,3 +3,4 @@
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
pub mod all; pub mod all;
pub mod soa;

View file

@ -0,0 +1,119 @@
use std::usize;
#[derive(PartialEq, Eq)]
pub struct Index<T> {
index: u32,
_marker: std::marker::PhantomData<T>,
}
impl<T> Clone for Index<T> {
fn clone(&self) -> Self {
Self {
index: self.index,
_marker: self._marker,
}
}
}
impl<T> Copy for Index<T> {}
impl<T> std::fmt::Debug for Index<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Index({})", self.index)
}
}
impl<T> Index<T> {
pub const fn new(index: u32) -> Self {
Self {
index,
_marker: std::marker::PhantomData,
}
}
pub const fn index(&self) -> usize {
self.index as usize
}
pub fn push_new(vector: &mut Vec<T>, value: T) -> Index<T> {
let index = Self::new(vector.len() as _);
vector.push(value);
index
}
}
#[derive(PartialEq, Eq)]
pub struct Slice<T> {
start: u32,
length: u16,
_marker: std::marker::PhantomData<T>,
}
impl<T> Clone for Slice<T> {
fn clone(&self) -> Self {
Self {
start: self.start,
length: self.length,
_marker: self._marker,
}
}
}
impl<T> Copy for Slice<T> {}
impl<T> std::fmt::Debug for Slice<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Slice(start = {}, length = {})", self.start, self.length)
}
}
impl<T> Default for Slice<T> {
fn default() -> Self {
Self {
start: Default::default(),
length: Default::default(),
_marker: Default::default(),
}
}
}
impl<T> Slice<T> {
pub const fn new(start: u32, length: u16) -> Self {
Self {
start,
length,
_marker: std::marker::PhantomData,
}
}
pub fn extend_new<I>(vector: &mut Vec<T>, values: I) -> Slice<T>
where
I: IntoIterator<Item = T>,
{
let start = vector.len() as u32;
vector.extend(values);
let end = vector.len() as u32;
Self::new(start, (end - start) as u16)
}
pub const fn len(&self) -> usize {
self.length as _
}
pub const fn is_empty(&self) -> bool {
self.length == 0
}
pub const fn indices(&self) -> std::ops::Range<usize> {
self.start as usize..(self.start as usize + self.length as usize)
}
pub fn into_iter(&self) -> impl Iterator<Item = Index<T>> {
self.indices().map(|i| Index::new(i as _))
}
}

View file

@ -14,3 +14,4 @@ roc_parse = { path = "../parse" }
roc_types = { path = "../types" } roc_types = { path = "../types" }
roc_can = { path = "../can" } roc_can = { path = "../can" }
roc_builtins = { path = "../builtins" } roc_builtins = { path = "../builtins" }
arrayvec = "0.7.2"

View file

@ -1,8 +1,7 @@
use roc_can::constraint::Constraint::{self, *}; use arrayvec::ArrayVec;
use roc_can::constraint::LetConstraint; use roc_can::constraint::{Constraint, Constraints};
use roc_can::expected::Expected::{self, *}; use roc_can::expected::Expected::{self, *};
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand}; use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
use roc_collections::all::SendMap;
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::Region; use roc_region::all::Region;
@ -12,8 +11,10 @@ use roc_types::types::Type::{self, *};
use roc_types::types::{AliasKind, Category}; use roc_types::types::{AliasKind, Category};
#[must_use] #[must_use]
#[inline(always)]
pub fn add_numeric_bound_constr( pub fn add_numeric_bound_constr(
constrs: &mut Vec<Constraint>, constraints: &mut Constraints,
num_constraints: &mut impl Extend<Constraint>,
num_type: Type, num_type: Type,
bound: impl TypedNumericBound, bound: impl TypedNumericBound,
region: Region, region: Region,
@ -27,12 +28,12 @@ pub fn add_numeric_bound_constr(
0 => total_num_type, 0 => total_num_type,
1 => { 1 => {
let actual_type = Variable(range[0]); let actual_type = Variable(range[0]);
constrs.push(Eq( let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
total_num_type.clone(), let because_suffix =
Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region), constraints.equal_types(total_num_type.clone(), expected, category, region);
category,
region, num_constraints.extend([because_suffix]);
));
total_num_type total_num_type
} }
_ => RangedNumber(Box::new(total_num_type), range), _ => RangedNumber(Box::new(total_num_type), range),
@ -41,6 +42,7 @@ pub fn add_numeric_bound_constr(
#[inline(always)] #[inline(always)]
pub fn int_literal( pub fn int_literal(
constraints: &mut Constraints,
num_var: Variable, num_var: Variable,
precision_var: Variable, precision_var: Variable,
expected: Expected<Type>, expected: Expected<Type>,
@ -49,31 +51,35 @@ pub fn int_literal(
) -> Constraint { ) -> Constraint {
let reason = Reason::IntLiteral; let reason = Reason::IntLiteral;
let mut constrs = Vec::with_capacity(3); // Always add the bound first; this improves the resolved type quality in case it's an alias like "U8".
// Always add the bound first; this improves the resolved type quality in case it's an alias let mut constrs = ArrayVec::<_, 3>::new();
// like "U8".
let num_type = add_numeric_bound_constr( let num_type = add_numeric_bound_constr(
constraints,
&mut constrs, &mut constrs,
Variable(num_var), Variable(num_var),
bound, bound,
region, region,
Category::Num, Category::Num,
); );
constrs.extend(vec![
Eq( constrs.extend([
constraints.equal_types(
num_type.clone(), num_type.clone(),
ForReason(reason, num_int(Type::Variable(precision_var)), region), ForReason(reason, num_int(Type::Variable(precision_var)), region),
Category::Int, Category::Int,
region, region,
), ),
Eq(num_type, expected, Category::Int, region), constraints.equal_types(num_type, expected, Category::Int, region),
]); ]);
exists(vec![num_var], And(constrs)) // TODO the precision_var is not part of the exists here; for float it is. Which is correct?
let and_constraint = constraints.and_constraint(constrs);
constraints.exists([num_var], and_constraint)
} }
#[inline(always)] #[inline(always)]
pub fn float_literal( pub fn float_literal(
constraints: &mut Constraints,
num_var: Variable, num_var: Variable,
precision_var: Variable, precision_var: Variable,
expected: Expected<Type>, expected: Expected<Type>,
@ -82,29 +88,33 @@ pub fn float_literal(
) -> Constraint { ) -> Constraint {
let reason = Reason::FloatLiteral; let reason = Reason::FloatLiteral;
let mut constrs = Vec::with_capacity(3); let mut constrs = ArrayVec::<_, 3>::new();
let num_type = add_numeric_bound_constr( let num_type = add_numeric_bound_constr(
constraints,
&mut constrs, &mut constrs,
Variable(num_var), Variable(num_var),
bound, bound,
region, region,
Category::Float, Category::Float,
); );
constrs.extend(vec![
Eq( constrs.extend([
constraints.equal_types(
num_type.clone(), num_type.clone(),
ForReason(reason, num_float(Type::Variable(precision_var)), region), ForReason(reason, num_float(Type::Variable(precision_var)), region),
Category::Float, Category::Float,
region, region,
), ),
Eq(num_type, expected, Category::Float, region), constraints.equal_types(num_type, expected, Category::Float, region),
]); ]);
exists(vec![num_var, precision_var], And(constrs)) let and_constraint = constraints.and_constraint(constrs);
constraints.exists([num_var, precision_var], and_constraint)
} }
#[inline(always)] #[inline(always)]
pub fn num_literal( pub fn num_literal(
constraints: &mut Constraints,
num_var: Variable, num_var: Variable,
expected: Expected<Type>, expected: Expected<Type>,
region: Region, region: Region,
@ -112,23 +122,20 @@ pub fn num_literal(
) -> Constraint { ) -> Constraint {
let open_number_type = crate::builtins::num_num(Type::Variable(num_var)); let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
let mut constrs = Vec::with_capacity(3); let mut constrs = ArrayVec::<_, 2>::new();
let num_type = let num_type = add_numeric_bound_constr(
add_numeric_bound_constr(&mut constrs, open_number_type, bound, region, Category::Num); constraints,
constrs.extend(vec![Eq(num_type, expected, Category::Num, region)]); &mut constrs,
open_number_type,
bound,
region,
Category::Num,
);
exists(vec![num_var], And(constrs)) constrs.extend([constraints.equal_types(num_type, expected, Category::Num, region)]);
}
#[inline(always)] let and_constraint = constraints.and_constraint(constrs);
pub fn exists(flex_vars: Vec<Variable>, constraint: Constraint) -> Constraint { constraints.exists([num_var], and_constraint)
Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars,
def_types: SendMap::default(),
defs_constraint: constraint,
ret_constraint: Constraint::True,
}))
} }
#[inline(always)] #[inline(always)]

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
use crate::expr::constrain_decls;
use roc_builtins::std::StdLib; use roc_builtins::std::StdLib;
use roc_can::constraint::{Constraint, LetConstraint}; use roc_can::constraint::{Constraint, Constraints};
use roc_can::def::Declaration; use roc_can::def::Declaration;
use roc_collections::all::{MutMap, MutSet, SendMap}; use roc_collections::all::{MutMap, MutSet, SendMap};
use roc_module::symbol::{ModuleId, Symbol}; use roc_module::symbol::{ModuleId, Symbol};
@ -17,13 +16,12 @@ pub enum ExposedModuleTypes {
Valid(MutMap<Symbol, SolvedType>, MutMap<Symbol, Alias>), Valid(MutMap<Symbol, SolvedType>, MutMap<Symbol, Alias>),
} }
pub struct ConstrainedModule { pub fn constrain_module(
pub unused_imports: MutMap<ModuleId, Region>, constraints: &mut Constraints,
pub constraint: Constraint, declarations: &[Declaration],
} home: ModuleId,
) -> Constraint {
pub fn constrain_module(declarations: &[Declaration], home: ModuleId) -> Constraint { crate::expr::constrain_decls(constraints, home, declarations)
constrain_decls(home, declarations)
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -33,11 +31,11 @@ pub struct Import {
} }
pub fn constrain_imported_values( pub fn constrain_imported_values(
constraints: &mut Constraints,
imports: Vec<Import>, imports: Vec<Import>,
body_con: Constraint, body_con: Constraint,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> (Vec<Variable>, Constraint) { ) -> (Vec<Variable>, Constraint) {
use Constraint::*;
let mut def_types = SendMap::default(); let mut def_types = SendMap::default();
let mut rigid_vars = Vec::new(); let mut rigid_vars = Vec::new();
@ -84,24 +82,19 @@ pub fn constrain_imported_values(
( (
rigid_vars.clone(), rigid_vars.clone(),
Let(Box::new(LetConstraint { constraints.let_constraint(rigid_vars, [], def_types, Constraint::True, body_con),
rigid_vars,
flex_vars: Vec::new(),
def_types,
defs_constraint: True,
ret_constraint: body_con,
})),
) )
} }
/// Run pre_constrain_imports to get imported_symbols and imported_aliases. /// Run pre_constrain_imports to get imported_symbols and imported_aliases.
pub fn constrain_imports( pub fn constrain_imports(
constraints: &mut Constraints,
imported_symbols: Vec<Import>, imported_symbols: Vec<Import>,
constraint: Constraint, constraint: Constraint,
var_store: &mut VarStore, var_store: &mut VarStore,
) -> Constraint { ) -> Constraint {
let (_introduced_rigids, constraint) = let (_introduced_rigids, constraint) =
constrain_imported_values(imported_symbols, constraint, var_store); constrain_imported_values(constraints, imported_symbols, constraint, var_store);
// TODO determine what to do with those rigids // TODO determine what to do with those rigids
// for var in introduced_rigids { // for var in introduced_rigids {

View file

@ -1,10 +1,10 @@
use crate::builtins; use crate::builtins;
use crate::expr::{constrain_expr, Env}; use crate::expr::{constrain_expr, Env};
use roc_can::constraint::{Constraint, PresenceConstraint}; use roc_can::constraint::{Constraint, Constraints};
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_can::pattern::Pattern::{self, *}; use roc_can::pattern::Pattern::{self, *};
use roc_can::pattern::{DestructType, RecordDestruct}; use roc_can::pattern::{DestructType, RecordDestruct};
use roc_collections::all::{Index, SendMap}; use roc_collections::all::{HumanIndex, SendMap};
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -153,6 +153,7 @@ fn headers_from_annotation_help(
/// initialize the Vecs in PatternState using with_capacity /// initialize the Vecs in PatternState using with_capacity
/// based on its knowledge of their lengths. /// based on its knowledge of their lengths.
pub fn constrain_pattern( pub fn constrain_pattern(
constraints: &mut Constraints,
env: &Env, env: &Env,
pattern: &Pattern, pattern: &Pattern,
region: Region, region: Region,
@ -167,20 +168,18 @@ pub fn constrain_pattern(
// A -> "" // A -> ""
// _ -> "" // _ -> ""
// so, we know that "x" (in this case, a tag union) must be open. // so, we know that "x" (in this case, a tag union) must be open.
state.constraints.push(Constraint::Present( state
expected.get_type(), .constraints
PresenceConstraint::IsOpen, .push(constraints.is_open_type(expected.get_type()));
));
} }
UnsupportedPattern(_) | MalformedPattern(_, _) | OpaqueNotInScope(..) => { UnsupportedPattern(_) | MalformedPattern(_, _) | OpaqueNotInScope(..) => {
// Erroneous patterns don't add any constraints. // Erroneous patterns don't add any constraints.
} }
Identifier(symbol) | Shadowed(_, _, symbol) => { Identifier(symbol) | Shadowed(_, _, symbol) => {
state.constraints.push(Constraint::Present( state
expected.get_type_ref().clone(), .constraints
PresenceConstraint::IsOpen, .push(constraints.is_open_type(expected.get_type_ref().clone()));
));
state.headers.insert( state.headers.insert(
*symbol, *symbol,
Loc { Loc {
@ -196,6 +195,7 @@ pub fn constrain_pattern(
let num_type = builtins::num_num(Type::Variable(var)); let num_type = builtins::num_num(Type::Variable(var));
let num_type = builtins::add_numeric_bound_constr( let num_type = builtins::add_numeric_bound_constr(
constraints,
&mut state.constraints, &mut state.constraints,
num_type, num_type,
bound, bound,
@ -203,11 +203,11 @@ pub fn constrain_pattern(
Category::Num, Category::Num,
); );
state.constraints.push(Constraint::Pattern( state.constraints.push(constraints.equal_pattern_types(
region,
PatternCategory::Num,
num_type, num_type,
expected, expected,
PatternCategory::Num,
region,
)); ));
} }
@ -215,6 +215,7 @@ pub fn constrain_pattern(
// First constraint on the free num var; this improves the resolved type quality in // First constraint on the free num var; this improves the resolved type quality in
// case the bound is an alias. // case the bound is an alias.
let num_type = builtins::add_numeric_bound_constr( let num_type = builtins::add_numeric_bound_constr(
constraints,
&mut state.constraints, &mut state.constraints,
Type::Variable(num_var), Type::Variable(num_var),
bound, bound,
@ -225,7 +226,7 @@ pub fn constrain_pattern(
// Link the free num var with the int var and our expectation. // Link the free num var with the int var and our expectation.
let int_type = builtins::num_int(Type::Variable(precision_var)); let int_type = builtins::num_int(Type::Variable(precision_var));
state.constraints.push(Constraint::Eq( state.constraints.push(constraints.equal_types(
num_type, // TODO check me if something breaks! num_type, // TODO check me if something breaks!
Expected::NoExpectation(int_type), Expected::NoExpectation(int_type),
Category::Int, Category::Int,
@ -233,11 +234,11 @@ pub fn constrain_pattern(
)); ));
// Also constrain the pattern against the num var, again to reuse aliases if they're present. // Also constrain the pattern against the num var, again to reuse aliases if they're present.
state.constraints.push(Constraint::Pattern( state.constraints.push(constraints.equal_pattern_types(
region,
PatternCategory::Int,
Type::Variable(num_var), Type::Variable(num_var),
expected, expected,
PatternCategory::Int,
region,
)); ));
} }
@ -245,6 +246,7 @@ pub fn constrain_pattern(
// First constraint on the free num var; this improves the resolved type quality in // First constraint on the free num var; this improves the resolved type quality in
// case the bound is an alias. // case the bound is an alias.
let num_type = builtins::add_numeric_bound_constr( let num_type = builtins::add_numeric_bound_constr(
constraints,
&mut state.constraints, &mut state.constraints,
Type::Variable(num_var), Type::Variable(num_var),
bound, bound,
@ -255,7 +257,7 @@ pub fn constrain_pattern(
// Link the free num var with the float var and our expectation. // Link the free num var with the float var and our expectation.
let float_type = builtins::num_float(Type::Variable(precision_var)); let float_type = builtins::num_float(Type::Variable(precision_var));
state.constraints.push(Constraint::Eq( state.constraints.push(constraints.equal_types(
num_type.clone(), // TODO check me if something breaks! num_type.clone(), // TODO check me if something breaks!
Expected::NoExpectation(float_type), Expected::NoExpectation(float_type),
Category::Float, Category::Float,
@ -263,29 +265,29 @@ pub fn constrain_pattern(
)); ));
// Also constrain the pattern against the num var, again to reuse aliases if they're present. // Also constrain the pattern against the num var, again to reuse aliases if they're present.
state.constraints.push(Constraint::Pattern( state.constraints.push(constraints.equal_pattern_types(
region,
PatternCategory::Float,
num_type, // TODO check me if something breaks! num_type, // TODO check me if something breaks!
expected, expected,
PatternCategory::Float,
region,
)); ));
} }
StrLiteral(_) => { StrLiteral(_) => {
state.constraints.push(Constraint::Pattern( state.constraints.push(constraints.equal_pattern_types(
region,
PatternCategory::Str,
builtins::str_type(), builtins::str_type(),
expected, expected,
PatternCategory::Str,
region,
)); ));
} }
SingleQuote(_) => { SingleQuote(_) => {
state.constraints.push(Constraint::Pattern( state.constraints.push(constraints.equal_pattern_types(
region,
PatternCategory::Character,
builtins::num_u32(), builtins::num_u32(),
expected, expected,
PatternCategory::Character,
region,
)); ));
} }
@ -322,36 +324,39 @@ pub fn constrain_pattern(
let field_type = match typ { let field_type = match typ {
DestructType::Guard(guard_var, loc_guard) => { DestructType::Guard(guard_var, loc_guard) => {
state.constraints.push(Constraint::Present( state.constraints.push(constraints.pattern_presence(
Type::Variable(*guard_var), Type::Variable(*guard_var),
PresenceConstraint::Pattern( PExpected::ForReason(
region, PReason::PatternGuard,
PatternCategory::PatternGuard, pat_type.clone(),
PExpected::ForReason( loc_guard.region,
PReason::PatternGuard,
pat_type.clone(),
loc_guard.region,
),
), ),
PatternCategory::PatternGuard,
region,
)); ));
state.vars.push(*guard_var); state.vars.push(*guard_var);
constrain_pattern(env, &loc_guard.value, loc_guard.region, expected, state); constrain_pattern(
constraints,
env,
&loc_guard.value,
loc_guard.region,
expected,
state,
);
RecordField::Demanded(pat_type) RecordField::Demanded(pat_type)
} }
DestructType::Optional(expr_var, loc_expr) => { DestructType::Optional(expr_var, loc_expr) => {
state.constraints.push(Constraint::Present( state.constraints.push(constraints.pattern_presence(
Type::Variable(*expr_var), Type::Variable(*expr_var),
PresenceConstraint::Pattern( PExpected::ForReason(
region, PReason::OptionalField,
PatternCategory::PatternDefault, pat_type.clone(),
PExpected::ForReason( loc_expr.region,
PReason::OptionalField,
pat_type.clone(),
loc_expr.region,
),
), ),
PatternCategory::PatternDefault,
region,
)); ));
state.vars.push(*expr_var); state.vars.push(*expr_var);
@ -362,8 +367,13 @@ pub fn constrain_pattern(
loc_expr.region, loc_expr.region,
); );
let expr_con = let expr_con = constrain_expr(
constrain_expr(env, loc_expr.region, &loc_expr.value, expr_expected); constraints,
env,
loc_expr.region,
&loc_expr.value,
expr_expected,
);
state.constraints.push(expr_con); state.constraints.push(expr_con);
RecordField::Optional(pat_type) RecordField::Optional(pat_type)
@ -381,16 +391,18 @@ pub fn constrain_pattern(
let record_type = Type::Record(field_types, Box::new(ext_type)); let record_type = Type::Record(field_types, Box::new(ext_type));
let whole_con = Constraint::Eq( let whole_con = constraints.equal_types(
Type::Variable(*whole_var), Type::Variable(*whole_var),
Expected::NoExpectation(record_type), Expected::NoExpectation(record_type),
Category::Storage(std::file!(), std::line!()), Category::Storage(std::file!(), std::line!()),
region, region,
); );
let record_con = Constraint::Present( let record_con = constraints.pattern_presence(
Type::Variable(*whole_var), Type::Variable(*whole_var),
PresenceConstraint::Pattern(region, PatternCategory::Record, expected), expected,
PatternCategory::Record,
region,
); );
state.constraints.push(whole_con); state.constraints.push(whole_con);
@ -412,29 +424,36 @@ pub fn constrain_pattern(
let expected = PExpected::ForReason( let expected = PExpected::ForReason(
PReason::TagArg { PReason::TagArg {
tag_name: tag_name.clone(), tag_name: tag_name.clone(),
index: Index::zero_based(index), index: HumanIndex::zero_based(index),
}, },
pattern_type, pattern_type,
region, region,
); );
constrain_pattern(env, &loc_pattern.value, loc_pattern.region, expected, state); constrain_pattern(
constraints,
env,
&loc_pattern.value,
loc_pattern.region,
expected,
state,
);
} }
let pat_category = PatternCategory::Ctor(tag_name.clone()); let pat_category = PatternCategory::Ctor(tag_name.clone());
let whole_con = Constraint::Present( let whole_con = constraints.includes_tag(
expected.clone().get_type(), expected.clone().get_type(),
PresenceConstraint::IncludesTag( tag_name.clone(),
tag_name.clone(), argument_types.clone(),
argument_types.clone(), pat_category.clone(),
region, region,
pat_category.clone(),
),
); );
let tag_con = Constraint::Present( let tag_con = constraints.pattern_presence(
Type::Variable(*whole_var), Type::Variable(*whole_var),
PresenceConstraint::Pattern(region, pat_category, expected), expected,
pat_category,
region,
); );
state.vars.push(*whole_var); state.vars.push(*whole_var);
@ -466,6 +485,7 @@ pub fn constrain_pattern(
// First, add a constraint for the argument "who" // First, add a constraint for the argument "who"
let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type.clone()); let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type.clone());
constrain_pattern( constrain_pattern(
constraints,
env, env,
&loc_arg_pattern.value, &loc_arg_pattern.value,
loc_arg_pattern.region, loc_arg_pattern.region,
@ -474,7 +494,7 @@ pub fn constrain_pattern(
); );
// Next, link `whole_var` to the opaque type of "@Id who" // Next, link `whole_var` to the opaque type of "@Id who"
let whole_con = Constraint::Eq( let whole_con = constraints.equal_types(
Type::Variable(*whole_var), Type::Variable(*whole_var),
Expected::NoExpectation(opaque_type), Expected::NoExpectation(opaque_type),
Category::Storage(std::file!(), std::line!()), Category::Storage(std::file!(), std::line!()),
@ -484,7 +504,7 @@ pub fn constrain_pattern(
// Link the entire wrapped opaque type (with the now-constrained argument) to the type // Link the entire wrapped opaque type (with the now-constrained argument) to the type
// variables of the opaque type // variables of the opaque type
// TODO: better expectation here // TODO: better expectation here
let link_type_variables_con = Constraint::Eq( let link_type_variables_con = constraints.equal_types(
(**specialized_def_type).clone(), (**specialized_def_type).clone(),
Expected::NoExpectation(arg_pattern_type), Expected::NoExpectation(arg_pattern_type),
Category::OpaqueWrap(*opaque), Category::OpaqueWrap(*opaque),
@ -492,9 +512,11 @@ pub fn constrain_pattern(
); );
// Next, link `whole_var` (the type of "@Id who") to the expected type // Next, link `whole_var` (the type of "@Id who") to the expected type
let opaque_pattern_con = Constraint::Present( let opaque_pattern_con = constraints.pattern_presence(
Type::Variable(*whole_var), Type::Variable(*whole_var),
PresenceConstraint::Pattern(region, PatternCategory::Opaque(*opaque), expected), expected,
PatternCategory::Opaque(*opaque),
region,
); );
state state

View file

@ -1,4 +1,4 @@
use roc_collections::all::{Index, MutMap}; use roc_collections::all::{HumanIndex, MutMap};
use roc_module::ident::{Lowercase, TagIdIntType, TagName}; use roc_module::ident::{Lowercase, TagIdIntType, TagName};
use roc_region::all::Region; use roc_region::all::Region;
use roc_std::RocDec; use roc_std::RocDec;
@ -70,7 +70,7 @@ pub enum Error {
Redundant { Redundant {
overall_region: Region, overall_region: Region,
branch_region: Region, branch_region: Region,
index: Index, index: HumanIndex,
}, },
} }

View file

@ -293,6 +293,9 @@ impl<'a> Formattable for TypeAnnotation<'a> {
SpaceBefore(ann, spaces) => { SpaceBefore(ann, spaces) => {
buf.newline(); buf.newline();
buf.indent(indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
ann.format_with_options(buf, parens, Newlines::No, indent) ann.format_with_options(buf, parens, Newlines::No, indent)
} }

View file

@ -2984,6 +2984,18 @@ mod test_fmt {
)); ));
} }
#[test]
fn multiline_higher_order_function() {
expr_formats_same(indoc!(
r#"
foo :
(Str -> Bool) -> Bool
42
"#
));
}
#[test] #[test]
/// Test that everything under examples/ is formatted correctly /// Test that everything under examples/ is formatted correctly
/// If this test fails on your diff, it probably means you need to re-format the examples. /// If this test fails on your diff, it probably means you need to re-format the examples.

View file

@ -5,14 +5,14 @@ use crossbeam::deque::{Injector, Stealer, Worker};
use crossbeam::thread; use crossbeam::thread;
use parking_lot::Mutex; use parking_lot::Mutex;
use roc_builtins::std::StdLib; use roc_builtins::std::StdLib;
use roc_can::constraint::Constraint; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
use roc_can::def::Declaration; use roc_can::def::Declaration;
use roc_can::module::{canonicalize_module_defs, Module}; use roc_can::module::{canonicalize_module_defs, Module};
use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet}; use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet};
use roc_constrain::module::{ use roc_constrain::module::{
constrain_imports, pre_constrain_imports, ConstrainableImports, Import, constrain_imports, constrain_module, pre_constrain_imports, ConstrainableImports,
ExposedModuleTypes, Import, SubsByModule,
}; };
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName}; use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
use roc_module::symbol::{ use roc_module::symbol::{
IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified, IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified,
@ -229,6 +229,7 @@ fn start_phase<'a>(
module, module,
ident_ids, ident_ids,
module_timing, module_timing,
constraints,
constraint, constraint,
var_store, var_store,
imported_modules, imported_modules,
@ -241,6 +242,7 @@ fn start_phase<'a>(
module, module,
ident_ids, ident_ids,
module_timing, module_timing,
constraints,
constraint, constraint,
var_store, var_store,
imported_modules, imported_modules,
@ -391,7 +393,8 @@ struct ConstrainedModule {
module: Module, module: Module,
declarations: Vec<Declaration>, declarations: Vec<Declaration>,
imported_modules: MutMap<ModuleId, Region>, imported_modules: MutMap<ModuleId, Region>,
constraint: Constraint, constraints: Constraints,
constraint: ConstraintSoa,
ident_ids: IdentIds, ident_ids: IdentIds,
var_store: VarStore, var_store: VarStore,
dep_idents: MutMap<ModuleId, IdentIds>, dep_idents: MutMap<ModuleId, IdentIds>,
@ -728,7 +731,8 @@ enum BuildTask<'a> {
ident_ids: IdentIds, ident_ids: IdentIds,
imported_symbols: Vec<Import>, imported_symbols: Vec<Import>,
module_timing: ModuleTiming, module_timing: ModuleTiming,
constraint: Constraint, constraints: Constraints,
constraint: ConstraintSoa,
var_store: VarStore, var_store: VarStore,
declarations: Vec<Declaration>, declarations: Vec<Declaration>,
dep_idents: MutMap<ModuleId, IdentIds>, dep_idents: MutMap<ModuleId, IdentIds>,
@ -3027,7 +3031,8 @@ impl<'a> BuildTask<'a> {
module: Module, module: Module,
ident_ids: IdentIds, ident_ids: IdentIds,
module_timing: ModuleTiming, module_timing: ModuleTiming,
constraint: Constraint, constraints: Constraints,
constraint: ConstraintSoa,
var_store: VarStore, var_store: VarStore,
imported_modules: MutMap<ModuleId, Region>, imported_modules: MutMap<ModuleId, Region>,
exposed_types: &mut SubsByModule, exposed_types: &mut SubsByModule,
@ -3057,6 +3062,7 @@ impl<'a> BuildTask<'a> {
module, module,
ident_ids, ident_ids,
imported_symbols, imported_symbols,
constraints,
constraint, constraint,
var_store, var_store,
declarations, declarations,
@ -3073,7 +3079,8 @@ fn run_solve<'a>(
ident_ids: IdentIds, ident_ids: IdentIds,
mut module_timing: ModuleTiming, mut module_timing: ModuleTiming,
imported_symbols: Vec<Import>, imported_symbols: Vec<Import>,
constraint: Constraint, mut constraints: Constraints,
constraint: ConstraintSoa,
mut var_store: VarStore, mut var_store: VarStore,
decls: Vec<Declaration>, decls: Vec<Declaration>,
dep_idents: MutMap<ModuleId, IdentIds>, dep_idents: MutMap<ModuleId, IdentIds>,
@ -3084,7 +3091,12 @@ fn run_solve<'a>(
// Finish constraining the module by wrapping the existing Constraint // Finish constraining the module by wrapping the existing Constraint
// in the ones we just computed. We can do this off the main thread. // in the ones we just computed. We can do this off the main thread.
let constraint = constrain_imports(imported_symbols, constraint, &mut var_store); let constraint = constrain_imports(
&mut constraints,
imported_symbols,
constraint,
&mut var_store,
);
let constrain_end = SystemTime::now(); let constrain_end = SystemTime::now();
@ -3097,12 +3109,11 @@ fn run_solve<'a>(
.. ..
} = module; } = module;
if false { // TODO
debug_assert!(constraint.validate(), "{:?}", &constraint); // if false { debug_assert!(constraint.validate(), "{:?}", &constraint); }
}
let (solved_subs, solved_env, problems) = let (solved_subs, solved_env, problems) =
roc_solve::module::run_solve(rigid_variables, constraint, var_store); roc_solve::module::run_solve(&constraints, constraint, rigid_variables, var_store);
let exposed_vars_by_symbol: Vec<_> = solved_env let exposed_vars_by_symbol: Vec<_> = solved_env
.vars_by_symbol() .vars_by_symbol()
@ -3247,7 +3258,9 @@ fn canonicalize_and_constrain<'a>(
} }
}; };
let constraint = constrain_module(&module_output.declarations, module_id); let mut constraints = Constraints::new();
let constraint =
constrain_module(&mut constraints, &module_output.declarations, module_id);
let module = Module { let module = Module {
module_id, module_id,
@ -3263,6 +3276,7 @@ fn canonicalize_and_constrain<'a>(
declarations: module_output.declarations, declarations: module_output.declarations,
imported_modules, imported_modules,
var_store, var_store,
constraints,
constraint, constraint,
ident_ids: module_output.ident_ids, ident_ids: module_output.ident_ids,
dep_idents, dep_idents,
@ -3745,6 +3759,7 @@ fn run_task<'a>(
module, module,
module_timing, module_timing,
imported_symbols, imported_symbols,
constraints,
constraint, constraint,
var_store, var_store,
ident_ids, ident_ids,
@ -3756,6 +3771,7 @@ fn run_task<'a>(
ident_ids, ident_ids,
module_timing, module_timing,
imported_symbols, imported_symbols,
constraints,
constraint, constraint,
var_store, var_store,
declarations, declarations,

View file

@ -1,5 +1,5 @@
use crate::ir::DestructType; use crate::ir::DestructType;
use roc_collections::all::Index; use roc_collections::all::HumanIndex;
use roc_exhaustive::{ use roc_exhaustive::{
is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union, is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
}; };
@ -189,7 +189,7 @@ fn to_nonredundant_rows(
return Err(Error::Redundant { return Err(Error::Redundant {
overall_region, overall_region,
branch_region: region, branch_region: region,
index: Index::zero_based(checked_rows.len()), index: HumanIndex::zero_based(checked_rows.len()),
}); });
} }
} }

View file

@ -1,5 +1,5 @@
use crate::solve; use crate::solve;
use roc_can::constraint::Constraint; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
@ -17,8 +17,9 @@ pub struct SolvedModule {
} }
pub fn run_solve( pub fn run_solve(
constraints: &Constraints,
constraint: ConstraintSoa,
rigid_variables: MutMap<Variable, Lowercase>, rigid_variables: MutMap<Variable, Lowercase>,
constraint: Constraint,
var_store: VarStore, var_store: VarStore,
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) { ) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
let env = solve::Env::default(); let env = solve::Env::default();
@ -34,7 +35,7 @@ pub fn run_solve(
let mut problems = Vec::new(); let mut problems = Vec::new();
// Run the solver to populate Subs. // Run the solver to populate Subs.
let (solved_subs, solved_env) = solve::run(&env, &mut problems, subs, &constraint); let (solved_subs, solved_env) = solve::run(constraints, &env, &mut problems, subs, &constraint);
(solved_subs, solved_env, problems) (solved_subs, solved_env, problems)
} }

View file

@ -1,8 +1,9 @@
use bumpalo::Bump; use bumpalo::Bump;
use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::{LetConstraint, PresenceConstraint}; use roc_can::constraint::{Constraints, LetConstraint};
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{MutMap, SendMap}; use roc_collections::all::MutMap;
use roc_collections::soa::{Index, Slice};
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -170,18 +171,20 @@ struct State {
} }
pub fn run( pub fn run(
constraints: &Constraints,
env: &Env, env: &Env,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
mut subs: Subs, mut subs: Subs,
constraint: &Constraint, constraint: &Constraint,
) -> (Solved<Subs>, Env) { ) -> (Solved<Subs>, Env) {
let env = run_in_place(env, problems, &mut subs, constraint); let env = run_in_place(constraints, env, problems, &mut subs, constraint);
(Solved(subs), env) (Solved(subs), env)
} }
/// Modify an existing subs in-place instead /// Modify an existing subs in-place instead
pub fn run_in_place( pub fn run_in_place(
constraints: &Constraints,
env: &Env, env: &Env,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
subs: &mut Subs, subs: &mut Subs,
@ -198,6 +201,7 @@ pub fn run_in_place(
let state = solve( let state = solve(
&arena, &arena,
constraints,
env, env,
state, state,
rank, rank,
@ -233,6 +237,7 @@ enum Work<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn solve( fn solve(
arena: &Bump, arena: &Bump,
constraints: &Constraints,
env: &Env, env: &Env,
mut state: State, mut state: State,
rank: Rank, rank: Rank,
@ -249,6 +254,7 @@ fn solve(
}; };
let mut stack = vec![initial]; let mut stack = vec![initial];
while let Some(work_item) = stack.pop() { while let Some(work_item) = stack.pop() {
let (env, rank, constraint) = match work_item { let (env, rank, constraint) = match work_item {
Work::Constraint { Work::Constraint {
@ -270,14 +276,17 @@ fn solve(
} }
Work::LetConSimple { env, rank, let_con } => { Work::LetConSimple { env, rank, let_con } => {
// NOTE be extremely careful with shadowing here // NOTE be extremely careful with shadowing here
let offset = let_con.defs_and_ret_constraint.index();
let ret_constraint = &constraints.constraints[offset + 1];
// Add a variable for each def to new_vars_by_env. // Add a variable for each def to new_vars_by_env.
let local_def_vars = LocalDefVarsVec::from_def_types( let local_def_vars = LocalDefVarsVec::from_def_types(
constraints,
rank, rank,
pools, pools,
cached_aliases, cached_aliases,
subs, subs,
&let_con.def_types, let_con.def_types,
); );
let mut new_env = env.clone(); let mut new_env = env.clone();
@ -289,31 +298,35 @@ fn solve(
stack.push(Work::Constraint { stack.push(Work::Constraint {
env: arena.alloc(new_env), env: arena.alloc(new_env),
rank, rank,
constraint: &let_con.ret_constraint, constraint: &ret_constraint,
}); });
continue; continue;
} }
Work::LetConComplex { env, rank, let_con } => { Work::LetConComplex { env, rank, let_con } => {
// NOTE be extremely careful with shadowing here // NOTE be extremely careful with shadowing here
let offset = let_con.defs_and_ret_constraint.index();
let ret_constraint = &constraints.constraints[offset + 1];
let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()];
let next_rank = rank.next(); let next_rank = rank.next();
let mark = state.mark; let mark = state.mark;
let saved_env = state.env; let saved_env = state.env;
let rigid_vars = &let_con.rigid_vars;
let young_mark = mark; let young_mark = mark;
let visit_mark = young_mark.next(); let visit_mark = young_mark.next();
let final_mark = visit_mark.next(); let final_mark = visit_mark.next();
// Add a variable for each def to local_def_vars. // Add a variable for each def to local_def_vars.
let local_def_vars = LocalDefVarsVec::from_def_types( let local_def_vars = LocalDefVarsVec::from_def_types(
constraints,
next_rank, next_rank,
pools, pools,
cached_aliases, cached_aliases,
subs, subs,
&let_con.def_types, let_con.def_types,
); );
debug_assert_eq!( debug_assert_eq!(
@ -379,7 +392,7 @@ fn solve(
stack.push(Work::Constraint { stack.push(Work::Constraint {
env: arena.alloc(new_env), env: arena.alloc(new_env),
rank, rank,
constraint: &let_con.ret_constraint, constraint: &ret_constraint,
}); });
state = state_for_ret_con; state = state_for_ret_con;
@ -397,7 +410,11 @@ fn solve(
copy copy
} }
Eq(typ, expectation, category, region) => { Eq(type_index, expectation_index, category_index, region) => {
let typ = &constraints.types[type_index.index()];
let expectation = &constraints.expectations[expectation_index.index()];
let category = &constraints.categories[category_index.index()];
let actual = type_to_var(subs, rank, pools, cached_aliases, typ); let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
let expected = type_to_var( let expected = type_to_var(
subs, subs,
@ -436,7 +453,9 @@ fn solve(
} }
} }
} }
Store(source, target, _filename, _linenr) => { Store(source_index, target, _filename, _linenr) => {
let source = &constraints.types[source_index.index()];
// a special version of Eq that is used to store types in the AST. // a special version of Eq that is used to store types in the AST.
// IT DOES NOT REPORT ERRORS! // IT DOES NOT REPORT ERRORS!
let actual = type_to_var(subs, rank, pools, cached_aliases, source); let actual = type_to_var(subs, rank, pools, cached_aliases, source);
@ -464,7 +483,7 @@ fn solve(
} }
} }
} }
Lookup(symbol, expectation, region) => { Lookup(symbol, expectation_index, region) => {
match env.get_var_by_symbol(symbol) { match env.get_var_by_symbol(symbol) {
Some(var) => { Some(var) => {
// Deep copy the vars associated with this symbol before unifying them. // Deep copy the vars associated with this symbol before unifying them.
@ -488,7 +507,9 @@ fn solve(
// then we copy from that module's Subs into our own. If the value // then we copy from that module's Subs into our own. If the value
// is being looked up in this module, then we use our Subs as both // is being looked up in this module, then we use our Subs as both
// the source and destination. // the source and destination.
let actual = deep_copy_var(subs, rank, pools, var); let actual = deep_copy_var_in(subs, rank, pools, var, arena);
let expectation = &constraints.expectations[expectation_index.index()];
let expected = type_to_var( let expected = type_to_var(
subs, subs,
rank, rank,
@ -496,6 +517,7 @@ fn solve(
cached_aliases, cached_aliases,
expectation.get_type_ref(), expectation.get_type_ref(),
); );
match unify(subs, actual, expected, Mode::EQ) { match unify(subs, actual, expected, Mode::EQ) {
Success(vars) => { Success(vars) => {
introduce(subs, rank, pools, &vars); introduce(subs, rank, pools, &vars);
@ -533,8 +555,9 @@ fn solve(
} }
} }
} }
And(sub_constraints) => { And(slice) => {
for sub_constraint in sub_constraints.iter().rev() { let it = constraints.constraints[slice.indices()].iter().rev();
for sub_constraint in it {
stack.push(Work::Constraint { stack.push(Work::Constraint {
env, env,
rank, rank,
@ -544,8 +567,12 @@ fn solve(
state state
} }
Pattern(region, category, typ, expectation) Pattern(type_index, expectation_index, category_index, region)
| Present(typ, PresenceConstraint::Pattern(region, category, expectation)) => { | PatternPresence(type_index, expectation_index, category_index, region) => {
let typ = &constraints.types[type_index.index()];
let expectation = &constraints.pattern_expectations[expectation_index.index()];
let category = &constraints.pattern_categories[category_index.index()];
let actual = type_to_var(subs, rank, pools, cached_aliases, typ); let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
let expected = type_to_var( let expected = type_to_var(
subs, subs,
@ -556,7 +583,7 @@ fn solve(
); );
let mode = match constraint { let mode = match constraint {
Present(_, _) => Mode::PRESENT, PatternPresence(..) => Mode::PRESENT,
_ => Mode::EQ, _ => Mode::EQ,
}; };
@ -589,16 +616,25 @@ fn solve(
} }
} }
} }
Let(let_con) => { Let(index) => {
if matches!(&let_con.ret_constraint, True) && let_con.rigid_vars.is_empty() { let let_con = &constraints.let_constraints[index.index()];
introduce(subs, rank, pools, &let_con.flex_vars);
let offset = let_con.defs_and_ret_constraint.index();
let defs_constraint = &constraints.constraints[offset];
let ret_constraint = &constraints.constraints[offset + 1];
let flex_vars = &constraints.variables[let_con.flex_vars.indices()];
let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()];
if matches!(&ret_constraint, True) && let_con.rigid_vars.is_empty() {
introduce(subs, rank, pools, &flex_vars);
// If the return expression is guaranteed to solve, // If the return expression is guaranteed to solve,
// solve the assignments themselves and move on. // solve the assignments themselves and move on.
stack.push(Work::Constraint { stack.push(Work::Constraint {
env, env,
rank, rank,
constraint: &let_con.defs_constraint, constraint: &defs_constraint,
}); });
state state
@ -612,14 +648,11 @@ fn solve(
stack.push(Work::Constraint { stack.push(Work::Constraint {
env, env,
rank, rank,
constraint: &let_con.defs_constraint, constraint: &defs_constraint,
}); });
state state
} else { } else {
let rigid_vars = &let_con.rigid_vars;
let flex_vars = &let_con.flex_vars;
// work in the next pool to localize header // work in the next pool to localize header
let next_rank = rank.next(); let next_rank = rank.next();
@ -657,13 +690,15 @@ fn solve(
stack.push(Work::Constraint { stack.push(Work::Constraint {
env, env,
rank: next_rank, rank: next_rank,
constraint: &let_con.defs_constraint, constraint: &defs_constraint,
}); });
state state
} }
} }
Present(typ, PresenceConstraint::IsOpen) => { IsOpenType(type_index) => {
let typ = &constraints.types[type_index.index()];
let actual = type_to_var(subs, rank, pools, cached_aliases, typ); let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
let mut new_desc = subs.get(actual); let mut new_desc = subs.get(actual);
match new_desc.content { match new_desc.content {
@ -685,13 +720,24 @@ fn solve(
} }
} }
} }
Present( IncludesTag(index) => {
typ, let includes_tag = &constraints.includes_tags[index.index()];
PresenceConstraint::IncludesTag(tag_name, tys, region, pattern_category),
) => { let roc_can::constraint::IncludesTag {
type_index,
tag_name,
types,
pattern_category,
region,
} = includes_tag;
let typ = &constraints.types[type_index.index()];
let tys = &constraints.types[types.indices()];
let pattern_category = &constraints.pattern_categories[pattern_category.index()];
let actual = type_to_var(subs, rank, pools, cached_aliases, typ); let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
let tag_ty = Type::TagUnion( let tag_ty = Type::TagUnion(
vec![(tag_name.clone(), tys.clone())], vec![(tag_name.clone(), tys.to_vec())],
Box::new(Type::EmptyTagUnion), Box::new(Type::EmptyTagUnion),
); );
let includes = type_to_var(subs, rank, pools, cached_aliases, &tag_ty); let includes = type_to_var(subs, rank, pools, cached_aliases, &tag_ty);
@ -763,22 +809,26 @@ impl<T> LocalDefVarsVec<T> {
impl LocalDefVarsVec<(Symbol, Loc<Variable>)> { impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
fn from_def_types( fn from_def_types(
constraints: &Constraints,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
cached_aliases: &mut MutMap<Symbol, Variable>, cached_aliases: &mut MutMap<Symbol, Variable>,
subs: &mut Subs, subs: &mut Subs,
def_types: &SendMap<Symbol, Loc<Type>>, def_types_slice: Slice<(Symbol, Loc<Index<Type>>)>,
) -> Self { ) -> Self {
let def_types = &constraints.def_types[def_types_slice.indices()];
let mut local_def_vars = Self::with_length(def_types.len()); let mut local_def_vars = Self::with_length(def_types.len());
for (symbol, loc_type) in def_types.iter() { for (symbol, loc_type_index) in def_types.iter() {
let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); let typ = &constraints.types[loc_type_index.value.index()];
let var = type_to_var(subs, rank, pools, cached_aliases, typ);
local_def_vars.push(( local_def_vars.push((
*symbol, *symbol,
Loc { Loc {
value: var, value: var,
region: loc_type.region, region: loc_type_index.region,
}, },
)); ));
} }
@ -790,21 +840,16 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
use std::cell::RefCell; use std::cell::RefCell;
std::thread_local! { std::thread_local! {
/// Scratchpad arena so we don't need to allocate a new one all the time /// Scratchpad arena so we don't need to allocate a new one all the time
static SCRATCHPAD: RefCell<bumpalo::Bump> = RefCell::new(bumpalo::Bump::with_capacity(4 * 1024)); static SCRATCHPAD: RefCell<Option<bumpalo::Bump>> = RefCell::new(Some(bumpalo::Bump::with_capacity(4 * 1024)));
} }
fn take_scratchpad() -> bumpalo::Bump { fn take_scratchpad() -> bumpalo::Bump {
let mut result = bumpalo::Bump::new(); SCRATCHPAD.with(|f| f.take().unwrap())
SCRATCHPAD.with(|f| {
result = f.replace(bumpalo::Bump::new());
});
result
} }
fn put_scratchpad(scratchpad: bumpalo::Bump) { fn put_scratchpad(scratchpad: bumpalo::Bump) {
SCRATCHPAD.with(|f| { SCRATCHPAD.with(|f| {
f.replace(scratchpad); f.replace(Some(scratchpad));
}); });
} }
@ -987,7 +1032,7 @@ fn type_to_variable<'a>(
return reserved; return reserved;
} else { } else {
// for any other rank, we need to copy; it takes care of adjusting the rank // for any other rank, we need to copy; it takes care of adjusting the rank
return deep_copy_var(subs, rank, pools, reserved); return deep_copy_var_in(subs, rank, pools, reserved, arena);
} }
} }
@ -1055,6 +1100,7 @@ fn type_to_variable<'a>(
} }
} }
#[inline(always)]
fn alias_to_var<'a>( fn alias_to_var<'a>(
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
@ -1084,6 +1130,7 @@ fn alias_to_var<'a>(
} }
} }
#[inline(always)]
fn roc_result_to_var<'a>( fn roc_result_to_var<'a>(
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
@ -1832,10 +1879,14 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
} }
} }
fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable) -> Variable { fn deep_copy_var_in(
let mut arena = take_scratchpad(); subs: &mut Subs,
rank: Rank,
let mut visited = bumpalo::collections::Vec::with_capacity_in(4 * 1024, &arena); pools: &mut Pools,
var: Variable,
arena: &Bump,
) -> Variable {
let mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena);
let copy = deep_copy_var_help(subs, rank, pools, &mut visited, var); let copy = deep_copy_var_help(subs, rank, pools, &mut visited, var);
@ -1851,9 +1902,6 @@ fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable)
} }
} }
arena.reset();
put_scratchpad(arena);
copy copy
} }
@ -2092,6 +2140,7 @@ fn deep_copy_var_help(
} }
} }
#[inline(always)]
fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) -> Variable { fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) -> Variable {
let descriptor = Descriptor { let descriptor = Descriptor {
content, content,

View file

@ -2,7 +2,7 @@ use crate::pretty_print::Parens;
use crate::subs::{ use crate::subs::{
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice, GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
}; };
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap}; use roc_collections::all::{HumanIndex, ImMap, ImSet, MutSet, SendMap};
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::called_via::CalledVia; use roc_module::called_via::CalledVia;
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName}; use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
@ -1203,14 +1203,14 @@ pub struct TagUnionStructure<'a> {
pub enum PReason { pub enum PReason {
TypedArg { TypedArg {
opt_name: Option<Symbol>, opt_name: Option<Symbol>,
index: Index, index: HumanIndex,
}, },
WhenMatch { WhenMatch {
index: Index, index: HumanIndex,
}, },
TagArg { TagArg {
tag_name: TagName, tag_name: TagName,
index: Index, index: HumanIndex,
}, },
PatternGuard, PatternGuard,
OptionalField, OptionalField,
@ -1219,12 +1219,12 @@ pub enum PReason {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnnotationSource { pub enum AnnotationSource {
TypedIfBranch { TypedIfBranch {
index: Index, index: HumanIndex,
num_branches: usize, num_branches: usize,
region: Region, region: Region,
}, },
TypedWhenBranch { TypedWhenBranch {
index: Index, index: HumanIndex,
region: Region, region: Region,
}, },
TypedBody { TypedBody {
@ -1246,7 +1246,7 @@ impl AnnotationSource {
pub enum Reason { pub enum Reason {
FnArg { FnArg {
name: Option<Symbol>, name: Option<Symbol>,
arg_index: Index, arg_index: HumanIndex,
}, },
FnCall { FnCall {
name: Option<Symbol>, name: Option<Symbol>,
@ -1254,28 +1254,28 @@ pub enum Reason {
}, },
LowLevelOpArg { LowLevelOpArg {
op: LowLevel, op: LowLevel,
arg_index: Index, arg_index: HumanIndex,
}, },
ForeignCallArg { ForeignCallArg {
foreign_symbol: ForeignSymbol, foreign_symbol: ForeignSymbol,
arg_index: Index, arg_index: HumanIndex,
}, },
FloatLiteral, FloatLiteral,
IntLiteral, IntLiteral,
NumLiteral, NumLiteral,
StrInterpolation, StrInterpolation,
WhenBranch { WhenBranch {
index: Index, index: HumanIndex,
}, },
WhenGuard, WhenGuard,
ExpectCondition, ExpectCondition,
IfCondition, IfCondition,
IfBranch { IfBranch {
index: Index, index: HumanIndex,
total_branches: usize, total_branches: usize,
}, },
ElemInList { ElemInList {
index: Index, index: HumanIndex,
}, },
RecordUpdateValue(Lowercase), RecordUpdateValue(Lowercase),
RecordUpdateKeys(Symbol, SendMap<Lowercase, Region>), RecordUpdateKeys(Symbol, SendMap<Lowercase, Region>),

View file

@ -3,7 +3,7 @@
To run, go to the project home directory and run: To run, go to the project home directory and run:
```bash ```bash
$ cargo run -- build --backend=wasm32 examples/hello-web/Hello.roc $ cargo run -- build --target=wasm32 examples/hello-web/Hello.roc
``` ```
Then `cd` into the example directory and run any web server that can handle WebAssembly. Then `cd` into the example directory and run any web server that can handle WebAssembly.

View file

@ -1,5 +1,5 @@
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{Index, MutSet, SendMap}; use roc_collections::all::{HumanIndex, MutSet, SendMap};
use roc_module::called_via::{BinOp, CalledVia}; use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::{Ident, IdentStr, Lowercase, TagName}; use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
@ -350,7 +350,7 @@ fn to_expr_report<'b>(
num_branches, num_branches,
.. ..
} if num_branches == 2 => alloc.concat(vec![ } if num_branches == 2 => alloc.concat(vec![
alloc.keyword(if index == Index::FIRST { alloc.keyword(if index == HumanIndex::FIRST {
"then" "then"
} else { } else {
"else" "else"
@ -1372,7 +1372,7 @@ fn to_pattern_report<'b>(
} }
} }
PReason::WhenMatch { index } => { PReason::WhenMatch { index } => {
if index == Index::FIRST { if index == HumanIndex::FIRST {
let doc = alloc.stack(vec![ let doc = alloc.stack(vec![
alloc alloc
.text("The 1st pattern in this ") .text("The 1st pattern in this ")

View file

@ -1,7 +1,7 @@
extern crate bumpalo; extern crate bumpalo;
use self::bumpalo::Bump; use self::bumpalo::Bump;
use roc_can::constraint::Constraint; use roc_can::constraint::{Constraint, Constraints};
use roc_can::env::Env; use roc_can::env::Env;
use roc_can::expected::Expected; use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output}; use roc_can::expr::{canonicalize_expr, Expr, Output};
@ -28,11 +28,12 @@ pub fn test_home() -> ModuleId {
pub fn infer_expr( pub fn infer_expr(
subs: Subs, subs: Subs,
problems: &mut Vec<solve::TypeError>, problems: &mut Vec<solve::TypeError>,
constraints: &Constraints,
constraint: &Constraint, constraint: &Constraint,
expr_var: Variable, expr_var: Variable,
) -> (Content, Subs) { ) -> (Content, Subs) {
let env = solve::Env::default(); let env = solve::Env::default();
let (solved, _) = solve::run(&env, problems, subs, constraint); let (solved, _) = solve::run(constraints, &env, problems, subs, constraint);
let content = solved let content = solved
.inner() .inner()
@ -96,6 +97,7 @@ pub struct CanExprOut {
pub var_store: VarStore, pub var_store: VarStore,
pub var: Variable, pub var: Variable,
pub constraint: Constraint, pub constraint: Constraint,
pub constraints: Constraints,
} }
#[derive(Debug)] #[derive(Debug)]
@ -152,7 +154,9 @@ pub fn can_expr_with<'a>(
&loc_expr.value, &loc_expr.value,
); );
let mut constraints = Constraints::new();
let constraint = constrain_expr( let constraint = constrain_expr(
&mut constraints,
&roc_constrain::expr::Env { &roc_constrain::expr::Env {
rigids: ImMap::default(), rigids: ImMap::default(),
home, home,
@ -174,7 +178,7 @@ pub fn can_expr_with<'a>(
//load builtin values //load builtin values
let (_introduced_rigids, constraint) = let (_introduced_rigids, constraint) =
constrain_imported_values(imports, constraint, &mut var_store); constrain_imported_values(&mut constraints, imports, constraint, &mut var_store);
let mut all_ident_ids = MutMap::default(); let mut all_ident_ids = MutMap::default();
@ -200,6 +204,7 @@ pub fn can_expr_with<'a>(
interns, interns,
var, var,
constraint, constraint,
constraints,
}) })
} }

View file

@ -62,6 +62,7 @@ mod test_reporting {
output, output,
var_store, var_store,
var, var,
constraints,
constraint, constraint,
home, home,
interns, interns,
@ -79,7 +80,8 @@ mod test_reporting {
} }
let mut unify_problems = Vec::new(); let mut unify_problems = Vec::new();
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); let (_content, mut subs) =
infer_expr(subs, &mut unify_problems, &constraints, &constraint, var);
name_all_type_vars(var, &mut subs); name_all_type_vars(var, &mut subs);