mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 08:17:40 +03:00
commit
4d692af639
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2674,6 +2674,7 @@ dependencies = [
|
||||
"roc_unify",
|
||||
"roc_uniq",
|
||||
"target-lexicon",
|
||||
"ven_graph",
|
||||
"wgpu",
|
||||
"wgpu_glyph",
|
||||
"winit",
|
||||
|
@ -27,6 +27,7 @@ roc_fmt = { path = "../compiler/fmt" }
|
||||
|
||||
roc_reporting = { path = "../compiler/reporting" }
|
||||
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
|
||||
ven_graph = { path = "../vendor/pathfinding" }
|
||||
im = "14" # im and im-rc should always have the same version!
|
||||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::pattern::{Pattern2, PatternId};
|
||||
use crate::pool::{NodeId, PoolStr, PoolVec};
|
||||
use crate::pool::{NodeId, PoolStr, PoolVec, ShallowClone};
|
||||
use crate::types::{Type2, TypeId};
|
||||
use arraystring::{typenum::U30, ArrayString};
|
||||
use roc_can::expr::Recursive;
|
||||
@ -204,9 +204,22 @@ pub enum Expr2 {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ValueDef {
|
||||
pattern: PatternId, // 4B
|
||||
expr_type: Option<(Type2, Rigids)>, // ?
|
||||
expr_var: Variable, // 4B
|
||||
pattern: PatternId, // 4B
|
||||
expr_type: Option<(TypeId, Rigids)>, // ?
|
||||
expr_var: Variable, // 4B
|
||||
}
|
||||
|
||||
impl ShallowClone for ValueDef {
|
||||
fn shallow_clone(&self) -> Self {
|
||||
Self {
|
||||
pattern: self.pattern,
|
||||
expr_type: match &self.expr_type {
|
||||
Some((id, rigids)) => Some((*id, rigids.shallow_clone())),
|
||||
None => None,
|
||||
},
|
||||
expr_var: self.expr_var,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -224,10 +237,38 @@ pub enum FunctionDef {
|
||||
},
|
||||
}
|
||||
|
||||
impl ShallowClone for FunctionDef {
|
||||
fn shallow_clone(&self) -> Self {
|
||||
match self {
|
||||
Self::WithAnnotation {
|
||||
name,
|
||||
arguments,
|
||||
rigids,
|
||||
return_type,
|
||||
} => Self::WithAnnotation {
|
||||
name: *name,
|
||||
arguments: arguments.shallow_clone(),
|
||||
rigids: *rigids,
|
||||
return_type: *return_type,
|
||||
},
|
||||
|
||||
Self::NoAnnotation {
|
||||
name,
|
||||
arguments,
|
||||
return_var,
|
||||
} => Self::NoAnnotation {
|
||||
name: *name,
|
||||
arguments: arguments.shallow_clone(),
|
||||
return_var: *return_var,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rigids {
|
||||
named: PoolVec<(PoolStr, Variable)>, // 8B
|
||||
unnamed: PoolVec<Variable>, // 8B
|
||||
pub named: PoolVec<(PoolStr, Variable)>, // 8B
|
||||
pub unnamed: PoolVec<Variable>, // 8B
|
||||
}
|
||||
|
||||
/// This is overflow data from a Closure variant, which needs to store
|
||||
@ -253,3 +294,12 @@ pub type ExprId = NodeId<Expr2>;
|
||||
fn size_of_expr() {
|
||||
assert_eq!(std::mem::size_of::<Expr2>(), crate::pool::NODE_BYTES);
|
||||
}
|
||||
|
||||
impl ShallowClone for Rigids {
|
||||
fn shallow_clone(&self) -> Self {
|
||||
Self {
|
||||
named: self.named.shallow_clone(),
|
||||
unnamed: self.unnamed.shallow_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
430
editor/src/def.rs
Normal file
430
editor/src/def.rs
Normal file
@ -0,0 +1,430 @@
|
||||
#![allow(clippy::all)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_variables)]
|
||||
// use crate::annotation::canonicalize_annotation;
|
||||
// use crate::annotation::IntroducedVariables;
|
||||
// use crate::env::Env;
|
||||
// use crate::expr::Expr::{self, *};
|
||||
// use crate::expr::{
|
||||
// canonicalize_expr, local_successors, references_from_call, references_from_local, Output,
|
||||
// Recursive,
|
||||
// };
|
||||
// use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern};
|
||||
// use crate::procedure::References;
|
||||
use crate::ast::{FunctionDef, Rigids, ValueDef};
|
||||
use crate::expr::Env;
|
||||
use crate::pattern::{
|
||||
symbols_and_variables_from_pattern, symbols_from_pattern, to_pattern_id, Pattern2, PatternId,
|
||||
};
|
||||
use crate::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone};
|
||||
use crate::scope::Scope;
|
||||
use crate::types::{to_annotation2, Alias, Annotation2, References, Signature, Type2, TypeId};
|
||||
use roc_can::expr::Output;
|
||||
use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast;
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use ven_graph::{strongly_connected_components, topological_sort_into_groups};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Def {
|
||||
AnnotationOnly {
|
||||
rigids: crate::ast::Rigids,
|
||||
annotation: TypeId,
|
||||
},
|
||||
Value(ValueDef),
|
||||
Function(FunctionDef),
|
||||
}
|
||||
|
||||
impl ShallowClone for Def {
|
||||
fn shallow_clone(&self) -> Self {
|
||||
match self {
|
||||
Self::AnnotationOnly { rigids, annotation } => Self::AnnotationOnly {
|
||||
rigids: rigids.shallow_clone(),
|
||||
annotation: *annotation,
|
||||
},
|
||||
Self::Value(def) => Self::Value(def.shallow_clone()),
|
||||
Self::Function(def) => Self::Function(def.shallow_clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Def that has had patterns and type annnotations canonicalized,
|
||||
/// but no Expr canonicalization has happened yet. Also, it has had spaces
|
||||
/// and nesting resolved, and knows whether annotations are standalone or not.
|
||||
#[derive(Debug)]
|
||||
enum PendingDef<'a> {
|
||||
/// A standalone annotation with no body
|
||||
AnnotationOnly(
|
||||
&'a Located<ast::Pattern<'a>>,
|
||||
PatternId,
|
||||
&'a Located<ast::TypeAnnotation<'a>>,
|
||||
),
|
||||
/// A body with no type annotation
|
||||
Body(
|
||||
&'a Located<ast::Pattern<'a>>,
|
||||
PatternId,
|
||||
&'a Located<ast::Expr<'a>>,
|
||||
),
|
||||
/// A body with a type annotation
|
||||
TypedBody(
|
||||
&'a Located<ast::Pattern<'a>>,
|
||||
PatternId,
|
||||
&'a Located<ast::TypeAnnotation<'a>>,
|
||||
&'a Located<ast::Expr<'a>>,
|
||||
),
|
||||
|
||||
/// A type alias, e.g. `Ints : List Int`
|
||||
Alias {
|
||||
name: Located<Symbol>,
|
||||
vars: Vec<Located<Lowercase>>,
|
||||
ann: &'a Located<ast::TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
/// An invalid alias, that is ignored in the rest of the pipeline
|
||||
/// e.g. a shadowed alias, or a definition like `MyAlias 1 : Int`
|
||||
/// with an incorrect pattern
|
||||
InvalidAlias,
|
||||
}
|
||||
|
||||
fn to_pending_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
def: &'a ast::Def<'a>,
|
||||
scope: &mut Scope,
|
||||
pattern_type: PatternType,
|
||||
) -> Option<(Output, PendingDef<'a>)> {
|
||||
use roc_parse::ast::Def::*;
|
||||
|
||||
match def {
|
||||
Annotation(loc_pattern, loc_ann) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = crate::pattern::to_pattern_id(
|
||||
env,
|
||||
scope,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
Some((
|
||||
output,
|
||||
PendingDef::AnnotationOnly(loc_pattern, loc_can_pattern, loc_ann),
|
||||
))
|
||||
}
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = crate::pattern::to_pattern_id(
|
||||
env,
|
||||
scope,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
Some((
|
||||
output,
|
||||
PendingDef::Body(loc_pattern, loc_can_pattern, loc_expr),
|
||||
))
|
||||
}
|
||||
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: _,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => {
|
||||
if ann_pattern.value.equivalent(&body_pattern.value) {
|
||||
// NOTE: Pick the body pattern, picking the annotation one is
|
||||
// incorrect in the presence of optional record fields!
|
||||
//
|
||||
// { x, y } : { x : Int, y ? Bool }*
|
||||
// { x, y ? False } = rec
|
||||
Some(pending_typed_body(
|
||||
env,
|
||||
body_pattern,
|
||||
ann_type,
|
||||
body_expr,
|
||||
scope,
|
||||
pattern_type,
|
||||
))
|
||||
} else {
|
||||
// the pattern of the annotation does not match the pattern of the body direc
|
||||
env.problem(Problem::SignatureDefMismatch {
|
||||
annotation_pattern: ann_pattern.region,
|
||||
def_pattern: body_pattern.region,
|
||||
});
|
||||
|
||||
// TODO: Should we instead build some PendingDef::InvalidAnnotatedBody ? This would
|
||||
// remove the `Option` on this function (and be probably more reliable for further
|
||||
// problem/error reporting)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
roc_parse::ast::Def::Alias { name, vars, ann } => {
|
||||
let region = Region::span_across(&name.region, &ann.region);
|
||||
|
||||
match scope.introduce(
|
||||
name.value.into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
region,
|
||||
) {
|
||||
Ok(symbol) => {
|
||||
let mut can_rigids: Vec<Located<Lowercase>> = Vec::with_capacity(vars.len());
|
||||
|
||||
for loc_var in vars.iter() {
|
||||
match loc_var.value {
|
||||
ast::Pattern::Identifier(name)
|
||||
if name.chars().next().unwrap().is_lowercase() =>
|
||||
{
|
||||
let lowercase = Lowercase::from(name);
|
||||
can_rigids.push(Located {
|
||||
value: lowercase,
|
||||
region: loc_var.region,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
// any other pattern in this position is a syntax error.
|
||||
env.problem(Problem::InvalidAliasRigid {
|
||||
alias_name: symbol,
|
||||
region: loc_var.region,
|
||||
});
|
||||
|
||||
return Some((Output::default(), PendingDef::InvalidAlias));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some((
|
||||
Output::default(),
|
||||
PendingDef::Alias {
|
||||
name: Located {
|
||||
region: name.region,
|
||||
value: symbol,
|
||||
},
|
||||
vars: can_rigids,
|
||||
ann,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
Err((original_region, loc_shadowed_symbol)) => {
|
||||
env.problem(Problem::ShadowingInAnnotation {
|
||||
original_region,
|
||||
shadow: loc_shadowed_symbol,
|
||||
});
|
||||
|
||||
Some((Output::default(), PendingDef::InvalidAlias))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) | Nested(sub_def) => {
|
||||
to_pending_def(env, sub_def, scope, pattern_type)
|
||||
}
|
||||
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_typed_body<'a>(
|
||||
env: &mut Env<'a>,
|
||||
loc_pattern: &'a Located<ast::Pattern<'a>>,
|
||||
loc_ann: &'a Located<ast::TypeAnnotation<'a>>,
|
||||
loc_expr: &'a Located<ast::Expr<'a>>,
|
||||
scope: &mut Scope,
|
||||
pattern_type: PatternType,
|
||||
) -> (Output, PendingDef<'a>) {
|
||||
// This takes care of checking for shadowing and adding idents to scope.
|
||||
let (output, loc_can_pattern) = to_pattern_id(
|
||||
env,
|
||||
scope,
|
||||
pattern_type,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
);
|
||||
|
||||
(
|
||||
output,
|
||||
PendingDef::TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO trim down these arguments!
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn canonicalize_pending_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
pending_def: PendingDef<'a>,
|
||||
mut output: Output,
|
||||
scope: &mut Scope,
|
||||
can_defs_by_symbol: &mut MutMap<Symbol, Def>,
|
||||
refs_by_symbol: &mut MutMap<Symbol, (Region, References)>,
|
||||
aliases: &mut SendMap<Symbol, Alias>,
|
||||
) -> Output {
|
||||
use PendingDef::*;
|
||||
|
||||
// Make types for the body expr, even if we won't end up having a body.
|
||||
let expr_var = env.var_store.fresh();
|
||||
|
||||
match pending_def {
|
||||
AnnotationOnly(_, loc_can_pattern, loc_ann) => {
|
||||
// annotation sans body cannot introduce new rigids that are visible in other annotations
|
||||
// but the rigids can show up in type error messages, so still register them
|
||||
|
||||
match to_annotation2(env, scope, &loc_ann.value, loc_ann.region) {
|
||||
Annotation2::Erroneous(_) => todo!(),
|
||||
Annotation2::Annotation {
|
||||
named_rigids,
|
||||
unnamed_rigids,
|
||||
symbols,
|
||||
signature,
|
||||
} => {
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
|
||||
for symbol in symbols {
|
||||
output.references.lookups.insert(symbol);
|
||||
output.references.referenced_aliases.insert(symbol);
|
||||
}
|
||||
|
||||
let rigids = {
|
||||
let named = PoolVec::with_capacity(named_rigids.len() as u32, env.pool);
|
||||
let unnamed = PoolVec::with_capacity(unnamed_rigids.len() as u32, env.pool);
|
||||
|
||||
for (node_id, (name, variable)) in named.iter_node_ids().zip(named_rigids) {
|
||||
let poolstr = PoolStr::new(name, env.pool);
|
||||
env.pool[node_id] = (poolstr, variable);
|
||||
}
|
||||
|
||||
for (node_id, rigid) in unnamed.iter_node_ids().zip(unnamed_rigids) {
|
||||
env.pool[node_id] = rigid;
|
||||
}
|
||||
|
||||
Rigids { named, unnamed }
|
||||
};
|
||||
|
||||
let annotation = match signature {
|
||||
Signature::Value { annotation } => annotation,
|
||||
Signature::Function {
|
||||
arguments,
|
||||
closure_type_id,
|
||||
return_type_id,
|
||||
} => Type2::Function(arguments, closure_type_id, return_type_id),
|
||||
Signature::FunctionWithAliases { annotation, .. } => annotation,
|
||||
};
|
||||
let annotation = env.add(annotation, loc_ann.region);
|
||||
|
||||
let def = Def::AnnotationOnly { rigids, annotation };
|
||||
|
||||
for symbol in symbols_from_pattern(env.pool, env.pool.get(loc_can_pattern)) {
|
||||
can_defs_by_symbol.insert(symbol, def.shallow_clone());
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PendingDef::Alias { name, ann, vars } => {
|
||||
let symbol = name.value;
|
||||
|
||||
match to_annotation2(env, scope, &ann.value, ann.region) {
|
||||
Annotation2::Erroneous(_) => todo!(),
|
||||
Annotation2::Annotation {
|
||||
named_rigids,
|
||||
unnamed_rigids,
|
||||
symbols,
|
||||
signature,
|
||||
} => {
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
|
||||
for symbol in symbols {
|
||||
output.references.lookups.insert(symbol);
|
||||
output.references.referenced_aliases.insert(symbol);
|
||||
}
|
||||
|
||||
for loc_lowercase in vars {
|
||||
if !named_rigids.contains_key(loc_lowercase.value.as_str()) {
|
||||
env.problem(Problem::PhantomTypeArgument {
|
||||
alias: symbol,
|
||||
variable_region: loc_lowercase.region,
|
||||
variable_name: loc_lowercase.value.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let named = PoolVec::with_capacity(named_rigids.len() as u32, env.pool);
|
||||
let unnamed = PoolVec::with_capacity(unnamed_rigids.len() as u32, env.pool);
|
||||
|
||||
for (node_id, (name, variable)) in named.iter_node_ids().zip(named_rigids) {
|
||||
let poolstr = PoolStr::new(name, env.pool);
|
||||
|
||||
env.pool[node_id] = (poolstr, variable);
|
||||
}
|
||||
|
||||
for (node_id, rigid) in unnamed.iter_node_ids().zip(unnamed_rigids) {
|
||||
env.pool[node_id] = rigid;
|
||||
}
|
||||
|
||||
let rigids = Rigids {
|
||||
named: named.shallow_clone(),
|
||||
unnamed,
|
||||
};
|
||||
|
||||
let annotation = match signature {
|
||||
Signature::Value { annotation } => annotation,
|
||||
Signature::Function {
|
||||
arguments,
|
||||
closure_type_id,
|
||||
return_type_id,
|
||||
} => Type2::Function(arguments, closure_type_id, return_type_id),
|
||||
Signature::FunctionWithAliases { annotation, .. } => annotation,
|
||||
};
|
||||
|
||||
if annotation.contains_symbol(env.pool, symbol) {
|
||||
// the alias is recursive. If it's a tag union, we attempt to fix this
|
||||
if let Type2::TagUnion(tags, ext) = annotation {
|
||||
// re-canonicalize the alias with the alias already in scope
|
||||
let rec_var = env.var_store.fresh();
|
||||
let rec_type_union = Type2::RecursiveTagUnion(rec_var, tags, ext);
|
||||
|
||||
// NOTE this only looks at the symbol, and just assumes that the
|
||||
// recursion is not polymorphic
|
||||
rec_type_union.substitute_alias(
|
||||
env.pool,
|
||||
symbol,
|
||||
Type2::Variable(rec_var),
|
||||
);
|
||||
|
||||
let annotation_id = env.add(rec_type_union, ann.region);
|
||||
scope.add_alias(env.pool, symbol, named, annotation_id);
|
||||
} else {
|
||||
env.problem(Problem::CyclicAlias(symbol, name.region, vec![]));
|
||||
return output;
|
||||
}
|
||||
} else {
|
||||
let annotation_id = env.add(annotation, ann.region);
|
||||
scope.add_alias(env.pool, symbol, named, annotation_id);
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InvalidAlias => {
|
||||
// invalid aliases (shadowed, incorrect patterns )
|
||||
todo!()
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
use crate::ast::{Expr2, ExprId, FloatVal, IntStyle, IntVal};
|
||||
use crate::pattern::to_pattern2;
|
||||
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
|
||||
use crate::scope::Scope;
|
||||
use crate::types::{Type2, TypeId};
|
||||
use bumpalo::Bump;
|
||||
use inlinable_string::InlinableString;
|
||||
@ -11,7 +12,6 @@ use roc_can::expr::Output;
|
||||
use roc_can::expr::Recursive;
|
||||
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
||||
use roc_can::procedure::References;
|
||||
use roc_can::scope::Scope;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::operator::CalledVia;
|
||||
@ -511,7 +511,7 @@ pub fn to_expr2<'a>(
|
||||
// Shadow `scope` to make sure we don't accidentally use the original one for the
|
||||
// rest of this block, but keep the original around for later diffing.
|
||||
let original_scope = scope;
|
||||
let mut scope = original_scope.clone();
|
||||
let mut scope = original_scope.duplicate();
|
||||
let can_args = PoolVec::with_capacity(loc_arg_patterns.len() as u32, env.pool);
|
||||
let mut output = Output::default();
|
||||
|
||||
@ -569,16 +569,16 @@ pub fn to_expr2<'a>(
|
||||
// Now that we've collected all the references, check to see if any of the args we defined
|
||||
// went unreferenced. If any did, report them as unused arguments.
|
||||
for (sub_symbol, region) in scope.symbols() {
|
||||
if !original_scope.contains_symbol(*sub_symbol) {
|
||||
if !output.references.has_lookup(*sub_symbol) {
|
||||
if !original_scope.contains_symbol(sub_symbol) {
|
||||
if !output.references.has_lookup(sub_symbol) {
|
||||
// The body never referenced this argument we declared. It's an unused argument!
|
||||
env.problem(Problem::UnusedArgument(symbol, *sub_symbol, *region));
|
||||
env.problem(Problem::UnusedArgument(symbol, sub_symbol, region));
|
||||
}
|
||||
|
||||
// We shouldn't ultimately count arguments as referenced locals. Otherwise,
|
||||
// we end up with weird conclusions like the expression (\x -> x + 1)
|
||||
// references the (nonexistant) local variable x!
|
||||
output.references.lookups.remove(sub_symbol);
|
||||
output.references.lookups.remove(&sub_symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1049,7 +1049,7 @@ fn canonicalize_when_branch<'a>(
|
||||
let patterns = PoolVec::with_capacity(branch.patterns.len() as u32, env.pool);
|
||||
|
||||
let original_scope = scope;
|
||||
let mut scope = original_scope.clone();
|
||||
let mut scope = original_scope.duplicate();
|
||||
|
||||
// TODO report symbols not bound in all patterns
|
||||
for (node_id, loc_pattern) in patterns.iter_node_ids().zip(branch.patterns.iter()) {
|
||||
@ -1089,13 +1089,13 @@ fn canonicalize_when_branch<'a>(
|
||||
// Now that we've collected all the references for this branch, check to see if
|
||||
// any of the new idents it defined were unused. If any were, report it.
|
||||
for (symbol, region) in scope.symbols() {
|
||||
let symbol = *symbol;
|
||||
let symbol = symbol;
|
||||
|
||||
if !output.references.has_lookup(symbol)
|
||||
&& !branch_output.references.has_lookup(symbol)
|
||||
&& !original_scope.contains_symbol(symbol)
|
||||
{
|
||||
env.problem(Problem::UnusedDef(symbol, *region));
|
||||
env.problem(Problem::UnusedDef(symbol, region));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ use winit::event_loop::ControlFlow;
|
||||
|
||||
pub mod ast;
|
||||
mod buffer;
|
||||
mod def;
|
||||
pub mod expr;
|
||||
pub mod file;
|
||||
mod keyboard_input;
|
||||
|
@ -4,9 +4,9 @@
|
||||
use crate::ast::{ExprId, FloatVal, IntVal};
|
||||
use crate::expr::{to_expr_id, Env};
|
||||
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
|
||||
use crate::scope::Scope;
|
||||
use roc_can::expr::{unescape_char, Output};
|
||||
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
||||
use roc_can::scope::Scope;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
use roc_parse::pattern::PatternType;
|
||||
@ -455,6 +455,54 @@ pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
|
||||
symbols
|
||||
}
|
||||
|
||||
pub fn symbols_and_variables_from_pattern(
|
||||
pool: &Pool,
|
||||
initial: &Pattern2,
|
||||
initial_var: Variable,
|
||||
) -> Vec<(Symbol, Variable)> {
|
||||
use Pattern2::*;
|
||||
let mut symbols = Vec::new();
|
||||
let mut stack = vec![(initial_var, initial)];
|
||||
|
||||
while let Some((variable, pattern)) = stack.pop() {
|
||||
match pattern {
|
||||
Identifier(symbol) => {
|
||||
symbols.push((*symbol, variable));
|
||||
}
|
||||
|
||||
GlobalTag { arguments, .. } | PrivateTag { arguments, .. } => {
|
||||
for (var, pat) in arguments.iter(pool) {
|
||||
stack.push((*var, pat));
|
||||
}
|
||||
}
|
||||
|
||||
RecordDestructure { destructs, .. } => {
|
||||
for destruct in destructs.iter(pool) {
|
||||
let destruct_type = pool.get(destruct.typ);
|
||||
|
||||
if let DestructType::Guard(_, subpattern_id) = &destruct_type {
|
||||
let subpattern = pool.get(*subpattern_id);
|
||||
stack.push((destruct.var, subpattern));
|
||||
} else {
|
||||
symbols.push((destruct.symbol, destruct.var));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NumLiteral(_, _)
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
| Shadowed { .. }
|
||||
| UnsupportedPattern(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
symbols
|
||||
}
|
||||
|
||||
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
|
||||
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
|
||||
fn unsupported_pattern<'a>(
|
||||
|
@ -244,8 +244,10 @@ impl PoolStr {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> Self {
|
||||
impl ShallowClone for PoolStr {
|
||||
fn shallow_clone(&self) -> Self {
|
||||
// Question: should this fully clone, or is a shallow copy
|
||||
// (and the aliasing it entails) OK?
|
||||
Self {
|
||||
@ -365,15 +367,6 @@ impl<'a, T: 'a + Sized> PoolVec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> Self {
|
||||
// Question: should this fully clone, or is a shallow copy
|
||||
// (and the aliasing it entails) OK?
|
||||
Self {
|
||||
first_node_id: self.first_node_id,
|
||||
len: self.len,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free<S>(self, pool: &'a mut Pool) {
|
||||
// zero out the memory
|
||||
unsafe {
|
||||
@ -388,6 +381,17 @@ impl<'a, T: 'a + Sized> PoolVec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ShallowClone for PoolVec<T> {
|
||||
fn shallow_clone(&self) -> Self {
|
||||
// Question: should this fully clone, or is a shallow copy
|
||||
// (and the aliasing it entails) OK?
|
||||
Self {
|
||||
first_node_id: self.first_node_id,
|
||||
len: self.len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PoolVecIter<'a, T> {
|
||||
pool: &'a Pool,
|
||||
current_node_id: NodeId<T>,
|
||||
@ -551,3 +555,8 @@ impl<T> Iterator for PoolVecIterNodeIds<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones the outer node, but does not clone any nodeids
|
||||
pub trait ShallowClone {
|
||||
fn shallow_clone(&self) -> Self;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#![allow(clippy::all)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use crate::pool::{Pool, PoolStr, PoolVec};
|
||||
use crate::pool::{Pool, PoolStr, PoolVec, ShallowClone};
|
||||
use crate::types::{Alias, TypeId};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::ident::{Ident, Lowercase};
|
||||
@ -25,7 +25,6 @@ pub struct Scope {
|
||||
symbols: MutMap<Symbol, Region>,
|
||||
|
||||
/// The type aliases currently in scope
|
||||
/// Uses BTreeMap because HashMap requires elements are Clone
|
||||
aliases: MutMap<Symbol, Alias>,
|
||||
|
||||
/// The current module being processed. This will be used to turn
|
||||
@ -34,6 +33,19 @@ pub struct Scope {
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn duplicate(&self) -> Self {
|
||||
Self {
|
||||
idents: self.idents.clone(),
|
||||
symbols: self.symbols.clone(),
|
||||
aliases: self
|
||||
.aliases
|
||||
.iter()
|
||||
.map(|(s, a)| (*s, a.shallow_clone()))
|
||||
.collect(),
|
||||
home: self.home,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(home: ModuleId, pool: &mut Pool, _var_store: &mut VarStore) -> Scope {
|
||||
use roc_types::solved_types::{BuiltinAlias, FreeVars};
|
||||
let solved_aliases = roc_types::builtin_aliases::aliases();
|
||||
|
@ -2,7 +2,7 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use crate::expr::Env;
|
||||
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
|
||||
use crate::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone};
|
||||
use crate::scope::Scope;
|
||||
// use roc_can::expr::Output;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
@ -45,7 +45,66 @@ impl Type2 {
|
||||
fn substitute(_pool: &mut Pool, _subs: &MutMap<Variable, TypeId>, _type_id: TypeId) {
|
||||
todo!()
|
||||
}
|
||||
pub fn variables(&self, _pool: &mut Pool) -> MutSet<Variable> {
|
||||
|
||||
pub fn variables(&self, pool: &mut Pool) -> MutSet<Variable> {
|
||||
use Type2::*;
|
||||
|
||||
let mut stack = vec![self];
|
||||
let mut result = MutSet::default();
|
||||
|
||||
while let Some(this) = stack.pop() {
|
||||
match this {
|
||||
Variable(v) => {
|
||||
result.insert(*v);
|
||||
}
|
||||
Alias(_, _, actual) | AsAlias(_, _, actual) => {
|
||||
stack.push(pool.get(*actual));
|
||||
}
|
||||
HostExposedAlias {
|
||||
actual_var, actual, ..
|
||||
} => {
|
||||
result.insert(*actual_var);
|
||||
stack.push(pool.get(*actual));
|
||||
}
|
||||
EmptyTagUnion | EmptyRec | Erroneous(_) => {}
|
||||
TagUnion(tags, ext) => {
|
||||
for (_, args) in tags.iter(pool) {
|
||||
stack.extend(args.iter(pool));
|
||||
}
|
||||
stack.push(pool.get(*ext));
|
||||
}
|
||||
RecursiveTagUnion(rec, tags, ext) => {
|
||||
for (_, args) in tags.iter(pool) {
|
||||
stack.extend(args.iter(pool));
|
||||
}
|
||||
stack.push(pool.get(*ext));
|
||||
result.insert(*rec);
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, field) in fields.iter(pool) {
|
||||
stack.push(field.as_inner());
|
||||
}
|
||||
stack.push(pool.get(*ext));
|
||||
}
|
||||
Function(args, closure, result) => {
|
||||
stack.extend(args.iter(pool));
|
||||
stack.push(pool.get(*closure));
|
||||
stack.push(pool.get(*result));
|
||||
}
|
||||
Apply(_, args) => {
|
||||
stack.extend(args.iter(pool));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn contains_symbol(&self, _pool: &mut Pool, _needle: Symbol) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn substitute_alias(&self, _pool: &mut Pool, _needle: Symbol, _actual: Self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@ -163,7 +222,7 @@ fn shallow_dealias<'a>(
|
||||
}
|
||||
Type2::Function(arguments, closure_type_id, return_type_id) => {
|
||||
let signature = Signature::FunctionWithAliases {
|
||||
arguments: arguments.duplicate(),
|
||||
arguments: arguments.shallow_clone(),
|
||||
closure_type_id: *closure_type_id,
|
||||
return_type_id: *return_type_id,
|
||||
annotation,
|
||||
@ -370,7 +429,7 @@ pub fn to_type2<'a>(
|
||||
let poolstr = PoolStr::new(var_name, env.pool);
|
||||
|
||||
let type_id = env.pool.add(Type2::Variable(*var));
|
||||
env.pool[var_id] = (poolstr.duplicate(), type_id);
|
||||
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
||||
|
||||
env.pool[named_id] = (poolstr, *var);
|
||||
env.set_region(named_id, loc_var.region);
|
||||
@ -381,7 +440,7 @@ pub fn to_type2<'a>(
|
||||
let poolstr = PoolStr::new(var_name, env.pool);
|
||||
|
||||
let type_id = env.pool.add(Type2::Variable(var));
|
||||
env.pool[var_id] = (poolstr.duplicate(), type_id);
|
||||
env.pool[var_id] = (poolstr.shallow_clone(), type_id);
|
||||
|
||||
env.pool[named_id] = (poolstr, var);
|
||||
env.set_region(named_id, loc_var.region);
|
||||
@ -700,7 +759,7 @@ fn to_type_apply<'a>(
|
||||
|
||||
for (node_id, (type_id, loc_var_id)) in it {
|
||||
let loc_var = &env.pool[loc_var_id];
|
||||
let name = loc_var.0.duplicate();
|
||||
let name = loc_var.0.shallow_clone();
|
||||
let var = loc_var.1;
|
||||
|
||||
env.pool[node_id] = (name, type_id);
|
||||
@ -715,7 +774,7 @@ fn to_type_apply<'a>(
|
||||
if let Type2::RecursiveTagUnion(rvar, ref tags, ext) = &mut env.pool[actual] {
|
||||
substitutions.insert(*rvar, fresh);
|
||||
|
||||
env.pool[actual] = Type2::RecursiveTagUnion(new, tags.duplicate(), *ext);
|
||||
env.pool[actual] = Type2::RecursiveTagUnion(new, tags.shallow_clone(), *ext);
|
||||
}
|
||||
|
||||
// make sure hidden variables are freshly instantiated
|
||||
@ -742,3 +801,13 @@ pub struct Alias {
|
||||
/// hidden type variables, like the closure variable in `a -> b`
|
||||
pub hidden_variables: PoolVec<Variable>,
|
||||
}
|
||||
|
||||
impl ShallowClone for Alias {
|
||||
fn shallow_clone(&self) -> Self {
|
||||
Self {
|
||||
targs: self.targs.shallow_clone(),
|
||||
hidden_variables: self.hidden_variables.shallow_clone(),
|
||||
actual: self.actual,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user