mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
Merge branch 'main' into fix_6606
This commit is contained in:
commit
388d43fc34
287 changed files with 7602 additions and 4265 deletions
|
@ -526,7 +526,7 @@ pub fn rebuild_host(
|
|||
// on windows, we need the nightly toolchain so we can use `-Z export-executable-symbols`
|
||||
// using `+nightly` only works when running cargo through rustup
|
||||
let mut cmd = rustup();
|
||||
cmd.args(["run", "nightly-2023-08-20", "cargo"]);
|
||||
cmd.args(["run", "nightly-2023-12-21", "cargo"]);
|
||||
|
||||
cmd
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
interface Bool
|
||||
exposes [Bool, Eq, true, false, and, or, not, isEq, isNotEq]
|
||||
imports []
|
||||
module [Bool, Eq, true, false, and, or, not, isEq, isNotEq]
|
||||
|
||||
## Defines a type that can be compared for total equality.
|
||||
##
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
## - Holding unknown Roc types when developing [platforms](https://github.com/roc-lang/roc/wiki/Roc-concepts-explained#platform).
|
||||
## - To improve performance in rare cases.
|
||||
##
|
||||
interface Box
|
||||
exposes [box, unbox]
|
||||
imports []
|
||||
module [box, unbox]
|
||||
|
||||
## Allocates a value on the heap. Boxing is an expensive process as it copies
|
||||
## the value from the stack to the heap. This may provide a performance
|
||||
|
|
|
@ -1,55 +1,53 @@
|
|||
interface Decode
|
||||
exposes [
|
||||
DecodeError,
|
||||
DecodeResult,
|
||||
Decoder,
|
||||
Decoding,
|
||||
DecoderFormatting,
|
||||
decoder,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
f32,
|
||||
f64,
|
||||
dec,
|
||||
bool,
|
||||
string,
|
||||
list,
|
||||
record,
|
||||
tuple,
|
||||
custom,
|
||||
decodeWith,
|
||||
fromBytesPartial,
|
||||
fromBytes,
|
||||
mapResult,
|
||||
]
|
||||
imports [
|
||||
List,
|
||||
Result.{ Result },
|
||||
Num.{
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
F32,
|
||||
F64,
|
||||
Dec,
|
||||
},
|
||||
Bool.{ Bool },
|
||||
]
|
||||
module [
|
||||
DecodeError,
|
||||
DecodeResult,
|
||||
Decoder,
|
||||
Decoding,
|
||||
DecoderFormatting,
|
||||
decoder,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
f32,
|
||||
f64,
|
||||
dec,
|
||||
bool,
|
||||
string,
|
||||
list,
|
||||
record,
|
||||
tuple,
|
||||
custom,
|
||||
decodeWith,
|
||||
fromBytesPartial,
|
||||
fromBytes,
|
||||
mapResult,
|
||||
]
|
||||
|
||||
import List
|
||||
import Result exposing [Result]
|
||||
import Num exposing [
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
F32,
|
||||
F64,
|
||||
Dec,
|
||||
]
|
||||
import Bool exposing [Bool]
|
||||
|
||||
## Error types when decoding a `List U8` of utf-8 bytes using a [Decoder]
|
||||
DecodeError : [TooShort]
|
||||
|
|
|
@ -1,43 +1,41 @@
|
|||
interface Dict
|
||||
exposes [
|
||||
Dict,
|
||||
empty,
|
||||
withCapacity,
|
||||
single,
|
||||
clear,
|
||||
capacity,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
len,
|
||||
isEmpty,
|
||||
get,
|
||||
contains,
|
||||
insert,
|
||||
remove,
|
||||
update,
|
||||
walk,
|
||||
walkUntil,
|
||||
keepIf,
|
||||
dropIf,
|
||||
toList,
|
||||
fromList,
|
||||
keys,
|
||||
values,
|
||||
insertAll,
|
||||
keepShared,
|
||||
removeAll,
|
||||
map,
|
||||
joinMap,
|
||||
]
|
||||
imports [
|
||||
Bool.{ Bool, Eq },
|
||||
Result.{ Result },
|
||||
List,
|
||||
Str,
|
||||
Num.{ U64, F32, U32, U8, I8 },
|
||||
Hash.{ Hasher, Hash },
|
||||
Inspect.{ Inspect, Inspector, InspectFormatter },
|
||||
]
|
||||
module [
|
||||
Dict,
|
||||
empty,
|
||||
withCapacity,
|
||||
single,
|
||||
clear,
|
||||
capacity,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
len,
|
||||
isEmpty,
|
||||
get,
|
||||
contains,
|
||||
insert,
|
||||
remove,
|
||||
update,
|
||||
walk,
|
||||
walkUntil,
|
||||
keepIf,
|
||||
dropIf,
|
||||
toList,
|
||||
fromList,
|
||||
keys,
|
||||
values,
|
||||
insertAll,
|
||||
keepShared,
|
||||
removeAll,
|
||||
map,
|
||||
joinMap,
|
||||
]
|
||||
|
||||
import Bool exposing [Bool, Eq]
|
||||
import Result exposing [Result]
|
||||
import List
|
||||
import Str
|
||||
import Num exposing [U64, F32, U32, U8]
|
||||
import Hash exposing [Hasher, Hash]
|
||||
import Inspect exposing [Inspect, Inspector, InspectFormatter]
|
||||
|
||||
## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you
|
||||
## associate keys with values.
|
||||
|
|
|
@ -1,51 +1,49 @@
|
|||
interface Encode
|
||||
exposes [
|
||||
Encoder,
|
||||
Encoding,
|
||||
toEncoder,
|
||||
EncoderFormatting,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
f32,
|
||||
f64,
|
||||
dec,
|
||||
bool,
|
||||
string,
|
||||
list,
|
||||
record,
|
||||
tag,
|
||||
tuple,
|
||||
custom,
|
||||
appendWith,
|
||||
append,
|
||||
toBytes,
|
||||
]
|
||||
imports [
|
||||
Num.{
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
F32,
|
||||
F64,
|
||||
Dec,
|
||||
},
|
||||
Bool.{ Bool },
|
||||
]
|
||||
module [
|
||||
Encoder,
|
||||
Encoding,
|
||||
toEncoder,
|
||||
EncoderFormatting,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
f32,
|
||||
f64,
|
||||
dec,
|
||||
bool,
|
||||
string,
|
||||
list,
|
||||
record,
|
||||
tag,
|
||||
tuple,
|
||||
custom,
|
||||
appendWith,
|
||||
append,
|
||||
toBytes,
|
||||
]
|
||||
|
||||
import Num exposing [
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
F32,
|
||||
F64,
|
||||
Dec,
|
||||
]
|
||||
import Bool exposing [Bool]
|
||||
|
||||
Encoder fmt := List U8, fmt -> List U8 where fmt implements EncoderFormatting
|
||||
|
||||
|
|
|
@ -1,31 +1,42 @@
|
|||
interface Hash
|
||||
exposes [
|
||||
Hash,
|
||||
Hasher,
|
||||
hash,
|
||||
addBytes,
|
||||
addU8,
|
||||
addU16,
|
||||
addU32,
|
||||
addU64,
|
||||
addU128,
|
||||
hashBool,
|
||||
hashI8,
|
||||
hashI16,
|
||||
hashI32,
|
||||
hashI64,
|
||||
hashI128,
|
||||
hashDec,
|
||||
complete,
|
||||
hashStrBytes,
|
||||
hashList,
|
||||
hashUnordered,
|
||||
] imports [
|
||||
Bool.{ Bool },
|
||||
List,
|
||||
Str,
|
||||
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, Dec },
|
||||
]
|
||||
module [
|
||||
Hash,
|
||||
Hasher,
|
||||
hash,
|
||||
addBytes,
|
||||
addU8,
|
||||
addU16,
|
||||
addU32,
|
||||
addU64,
|
||||
addU128,
|
||||
hashBool,
|
||||
hashI8,
|
||||
hashI16,
|
||||
hashI32,
|
||||
hashI64,
|
||||
hashI128,
|
||||
hashDec,
|
||||
complete,
|
||||
hashStrBytes,
|
||||
hashList,
|
||||
hashUnordered,
|
||||
]
|
||||
|
||||
import Bool exposing [Bool]
|
||||
import List
|
||||
import Str
|
||||
import Num exposing [
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
Dec,
|
||||
]
|
||||
|
||||
## A value that can be hashed.
|
||||
Hash implements
|
||||
|
|
|
@ -1,46 +1,44 @@
|
|||
interface Inspect
|
||||
exposes [
|
||||
Inspect,
|
||||
Inspector,
|
||||
InspectFormatter,
|
||||
ElemWalker,
|
||||
KeyValWalker,
|
||||
inspect,
|
||||
init,
|
||||
list,
|
||||
set,
|
||||
dict,
|
||||
tag,
|
||||
tuple,
|
||||
record,
|
||||
bool,
|
||||
str,
|
||||
function,
|
||||
opaque,
|
||||
u8,
|
||||
i8,
|
||||
u16,
|
||||
i16,
|
||||
u32,
|
||||
i32,
|
||||
u64,
|
||||
i64,
|
||||
u128,
|
||||
i128,
|
||||
f32,
|
||||
f64,
|
||||
dec,
|
||||
custom,
|
||||
apply,
|
||||
toInspector,
|
||||
toStr,
|
||||
]
|
||||
imports [
|
||||
Bool.{ Bool },
|
||||
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec },
|
||||
List,
|
||||
Str,
|
||||
]
|
||||
module [
|
||||
Inspect,
|
||||
Inspector,
|
||||
InspectFormatter,
|
||||
ElemWalker,
|
||||
KeyValWalker,
|
||||
inspect,
|
||||
init,
|
||||
list,
|
||||
set,
|
||||
dict,
|
||||
tag,
|
||||
tuple,
|
||||
record,
|
||||
bool,
|
||||
str,
|
||||
function,
|
||||
opaque,
|
||||
u8,
|
||||
i8,
|
||||
u16,
|
||||
i16,
|
||||
u32,
|
||||
i32,
|
||||
u64,
|
||||
i64,
|
||||
u128,
|
||||
i128,
|
||||
f32,
|
||||
f64,
|
||||
dec,
|
||||
custom,
|
||||
apply,
|
||||
toInspector,
|
||||
toStr,
|
||||
]
|
||||
|
||||
import Bool exposing [Bool]
|
||||
import Num exposing [U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec]
|
||||
import List
|
||||
import Str
|
||||
|
||||
KeyValWalker state collection key val : collection, state, (state, key, val -> state) -> state
|
||||
ElemWalker state collection elem : collection, state, (state, elem -> state) -> state
|
||||
|
|
|
@ -1,81 +1,79 @@
|
|||
interface List
|
||||
exposes [
|
||||
isEmpty,
|
||||
get,
|
||||
set,
|
||||
replace,
|
||||
update,
|
||||
append,
|
||||
appendIfOk,
|
||||
prepend,
|
||||
prependIfOk,
|
||||
map,
|
||||
len,
|
||||
withCapacity,
|
||||
walkBackwards,
|
||||
concat,
|
||||
first,
|
||||
single,
|
||||
repeat,
|
||||
reverse,
|
||||
join,
|
||||
keepIf,
|
||||
contains,
|
||||
sum,
|
||||
walk,
|
||||
last,
|
||||
keepOks,
|
||||
keepErrs,
|
||||
mapWithIndex,
|
||||
map2,
|
||||
map3,
|
||||
product,
|
||||
walkWithIndex,
|
||||
walkUntil,
|
||||
walkWithIndexUntil,
|
||||
walkFrom,
|
||||
walkFromUntil,
|
||||
range,
|
||||
sortWith,
|
||||
swap,
|
||||
dropAt,
|
||||
min,
|
||||
max,
|
||||
map4,
|
||||
mapTry,
|
||||
walkTry,
|
||||
joinMap,
|
||||
any,
|
||||
takeFirst,
|
||||
takeLast,
|
||||
dropFirst,
|
||||
dropLast,
|
||||
findFirst,
|
||||
findLast,
|
||||
findFirstIndex,
|
||||
findLastIndex,
|
||||
sublist,
|
||||
intersperse,
|
||||
split,
|
||||
splitFirst,
|
||||
splitLast,
|
||||
startsWith,
|
||||
endsWith,
|
||||
all,
|
||||
dropIf,
|
||||
sortAsc,
|
||||
sortDesc,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
walkBackwardsUntil,
|
||||
countIf,
|
||||
chunksOf,
|
||||
]
|
||||
imports [
|
||||
Bool.{ Bool, Eq },
|
||||
Result.{ Result },
|
||||
Num.{ U64, Num, Int },
|
||||
]
|
||||
module [
|
||||
isEmpty,
|
||||
get,
|
||||
set,
|
||||
replace,
|
||||
update,
|
||||
append,
|
||||
appendIfOk,
|
||||
prepend,
|
||||
prependIfOk,
|
||||
map,
|
||||
len,
|
||||
withCapacity,
|
||||
walkBackwards,
|
||||
concat,
|
||||
first,
|
||||
single,
|
||||
repeat,
|
||||
reverse,
|
||||
join,
|
||||
keepIf,
|
||||
contains,
|
||||
sum,
|
||||
walk,
|
||||
last,
|
||||
keepOks,
|
||||
keepErrs,
|
||||
mapWithIndex,
|
||||
map2,
|
||||
map3,
|
||||
product,
|
||||
walkWithIndex,
|
||||
walkUntil,
|
||||
walkWithIndexUntil,
|
||||
walkFrom,
|
||||
walkFromUntil,
|
||||
range,
|
||||
sortWith,
|
||||
swap,
|
||||
dropAt,
|
||||
min,
|
||||
max,
|
||||
map4,
|
||||
mapTry,
|
||||
walkTry,
|
||||
joinMap,
|
||||
any,
|
||||
takeFirst,
|
||||
takeLast,
|
||||
dropFirst,
|
||||
dropLast,
|
||||
findFirst,
|
||||
findLast,
|
||||
findFirstIndex,
|
||||
findLastIndex,
|
||||
sublist,
|
||||
intersperse,
|
||||
split,
|
||||
splitFirst,
|
||||
splitLast,
|
||||
startsWith,
|
||||
endsWith,
|
||||
all,
|
||||
dropIf,
|
||||
sortAsc,
|
||||
sortDesc,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
walkBackwardsUntil,
|
||||
countIf,
|
||||
chunksOf,
|
||||
]
|
||||
|
||||
import Bool exposing [Bool, Eq]
|
||||
import Result exposing [Result]
|
||||
import Num exposing [U64, Num]
|
||||
|
||||
## ## Types
|
||||
##
|
||||
|
|
|
@ -1,166 +1,164 @@
|
|||
interface Num
|
||||
exposes [
|
||||
Num,
|
||||
Int,
|
||||
Frac,
|
||||
Integer,
|
||||
FloatingPoint,
|
||||
I128,
|
||||
I64,
|
||||
I32,
|
||||
I16,
|
||||
I8,
|
||||
U128,
|
||||
U64,
|
||||
U32,
|
||||
U16,
|
||||
U8,
|
||||
Signed128,
|
||||
Signed64,
|
||||
Signed32,
|
||||
Signed16,
|
||||
Signed8,
|
||||
Unsigned128,
|
||||
Unsigned64,
|
||||
Unsigned32,
|
||||
Unsigned16,
|
||||
Unsigned8,
|
||||
Dec,
|
||||
F64,
|
||||
F32,
|
||||
Decimal,
|
||||
Binary32,
|
||||
Binary64,
|
||||
e,
|
||||
pi,
|
||||
tau,
|
||||
abs,
|
||||
absDiff,
|
||||
neg,
|
||||
add,
|
||||
sub,
|
||||
mul,
|
||||
min,
|
||||
max,
|
||||
isLt,
|
||||
isLte,
|
||||
isGt,
|
||||
isGte,
|
||||
isApproxEq,
|
||||
sin,
|
||||
cos,
|
||||
tan,
|
||||
atan,
|
||||
acos,
|
||||
asin,
|
||||
isZero,
|
||||
isEven,
|
||||
isOdd,
|
||||
toFrac,
|
||||
isPositive,
|
||||
isNegative,
|
||||
isNaN,
|
||||
isInfinite,
|
||||
isFinite,
|
||||
rem,
|
||||
remChecked,
|
||||
div,
|
||||
divChecked,
|
||||
sqrt,
|
||||
sqrtChecked,
|
||||
log,
|
||||
logChecked,
|
||||
round,
|
||||
ceiling,
|
||||
floor,
|
||||
compare,
|
||||
pow,
|
||||
powInt,
|
||||
countLeadingZeroBits,
|
||||
countTrailingZeroBits,
|
||||
countOneBits,
|
||||
addWrap,
|
||||
addChecked,
|
||||
addSaturated,
|
||||
bitwiseAnd,
|
||||
bitwiseXor,
|
||||
bitwiseOr,
|
||||
bitwiseNot,
|
||||
shiftLeftBy,
|
||||
shiftRightBy,
|
||||
shiftRightZfBy,
|
||||
subWrap,
|
||||
subChecked,
|
||||
subSaturated,
|
||||
mulWrap,
|
||||
mulSaturated,
|
||||
mulChecked,
|
||||
intCast,
|
||||
divCeil,
|
||||
divCeilChecked,
|
||||
divTrunc,
|
||||
divTruncChecked,
|
||||
toStr,
|
||||
isMultipleOf,
|
||||
minI8,
|
||||
maxI8,
|
||||
minU8,
|
||||
maxU8,
|
||||
minI16,
|
||||
maxI16,
|
||||
minU16,
|
||||
maxU16,
|
||||
minI32,
|
||||
maxI32,
|
||||
minU32,
|
||||
maxU32,
|
||||
minI64,
|
||||
maxI64,
|
||||
minU64,
|
||||
maxU64,
|
||||
minI128,
|
||||
maxI128,
|
||||
minU128,
|
||||
maxU128,
|
||||
minF32,
|
||||
maxF32,
|
||||
minF64,
|
||||
maxF64,
|
||||
toI8,
|
||||
toI8Checked,
|
||||
toI16,
|
||||
toI16Checked,
|
||||
toI32,
|
||||
toI32Checked,
|
||||
toI64,
|
||||
toI64Checked,
|
||||
toI128,
|
||||
toI128Checked,
|
||||
toU8,
|
||||
toU8Checked,
|
||||
toU16,
|
||||
toU16Checked,
|
||||
toU32,
|
||||
toU32Checked,
|
||||
toU64,
|
||||
toU64Checked,
|
||||
toU128,
|
||||
toU128Checked,
|
||||
toF32,
|
||||
toF32Checked,
|
||||
toF64,
|
||||
toF64Checked,
|
||||
withoutDecimalPoint,
|
||||
withDecimalPoint,
|
||||
f32ToParts,
|
||||
f64ToParts,
|
||||
f32FromParts,
|
||||
f64FromParts,
|
||||
]
|
||||
imports [
|
||||
Bool.{ Bool },
|
||||
Result.{ Result },
|
||||
]
|
||||
module [
|
||||
Num,
|
||||
Int,
|
||||
Frac,
|
||||
Integer,
|
||||
FloatingPoint,
|
||||
I128,
|
||||
I64,
|
||||
I32,
|
||||
I16,
|
||||
I8,
|
||||
U128,
|
||||
U64,
|
||||
U32,
|
||||
U16,
|
||||
U8,
|
||||
Signed128,
|
||||
Signed64,
|
||||
Signed32,
|
||||
Signed16,
|
||||
Signed8,
|
||||
Unsigned128,
|
||||
Unsigned64,
|
||||
Unsigned32,
|
||||
Unsigned16,
|
||||
Unsigned8,
|
||||
Dec,
|
||||
F64,
|
||||
F32,
|
||||
Decimal,
|
||||
Binary32,
|
||||
Binary64,
|
||||
e,
|
||||
pi,
|
||||
tau,
|
||||
abs,
|
||||
absDiff,
|
||||
neg,
|
||||
add,
|
||||
sub,
|
||||
mul,
|
||||
min,
|
||||
max,
|
||||
isLt,
|
||||
isLte,
|
||||
isGt,
|
||||
isGte,
|
||||
isApproxEq,
|
||||
sin,
|
||||
cos,
|
||||
tan,
|
||||
atan,
|
||||
acos,
|
||||
asin,
|
||||
isZero,
|
||||
isEven,
|
||||
isOdd,
|
||||
toFrac,
|
||||
isPositive,
|
||||
isNegative,
|
||||
isNaN,
|
||||
isInfinite,
|
||||
isFinite,
|
||||
rem,
|
||||
remChecked,
|
||||
div,
|
||||
divChecked,
|
||||
sqrt,
|
||||
sqrtChecked,
|
||||
log,
|
||||
logChecked,
|
||||
round,
|
||||
ceiling,
|
||||
floor,
|
||||
compare,
|
||||
pow,
|
||||
powInt,
|
||||
countLeadingZeroBits,
|
||||
countTrailingZeroBits,
|
||||
countOneBits,
|
||||
addWrap,
|
||||
addChecked,
|
||||
addSaturated,
|
||||
bitwiseAnd,
|
||||
bitwiseXor,
|
||||
bitwiseOr,
|
||||
bitwiseNot,
|
||||
shiftLeftBy,
|
||||
shiftRightBy,
|
||||
shiftRightZfBy,
|
||||
subWrap,
|
||||
subChecked,
|
||||
subSaturated,
|
||||
mulWrap,
|
||||
mulSaturated,
|
||||
mulChecked,
|
||||
intCast,
|
||||
divCeil,
|
||||
divCeilChecked,
|
||||
divTrunc,
|
||||
divTruncChecked,
|
||||
toStr,
|
||||
isMultipleOf,
|
||||
minI8,
|
||||
maxI8,
|
||||
minU8,
|
||||
maxU8,
|
||||
minI16,
|
||||
maxI16,
|
||||
minU16,
|
||||
maxU16,
|
||||
minI32,
|
||||
maxI32,
|
||||
minU32,
|
||||
maxU32,
|
||||
minI64,
|
||||
maxI64,
|
||||
minU64,
|
||||
maxU64,
|
||||
minI128,
|
||||
maxI128,
|
||||
minU128,
|
||||
maxU128,
|
||||
minF32,
|
||||
maxF32,
|
||||
minF64,
|
||||
maxF64,
|
||||
toI8,
|
||||
toI8Checked,
|
||||
toI16,
|
||||
toI16Checked,
|
||||
toI32,
|
||||
toI32Checked,
|
||||
toI64,
|
||||
toI64Checked,
|
||||
toI128,
|
||||
toI128Checked,
|
||||
toU8,
|
||||
toU8Checked,
|
||||
toU16,
|
||||
toU16Checked,
|
||||
toU32,
|
||||
toU32Checked,
|
||||
toU64,
|
||||
toU64Checked,
|
||||
toU128,
|
||||
toU128Checked,
|
||||
toF32,
|
||||
toF32Checked,
|
||||
toF64,
|
||||
toF64Checked,
|
||||
withoutDecimalPoint,
|
||||
withDecimalPoint,
|
||||
f32ToParts,
|
||||
f64ToParts,
|
||||
f32FromParts,
|
||||
f64FromParts,
|
||||
]
|
||||
|
||||
import Bool exposing [Bool]
|
||||
import Result exposing [Result]
|
||||
|
||||
## Represents a number that could be either an [Int] or a [Frac].
|
||||
##
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Result
|
||||
exposes [Result, isOk, isErr, map, mapErr, try, onErr, withDefault]
|
||||
imports [Bool.{ Bool }]
|
||||
module [Result, isOk, isErr, map, mapErr, try, onErr, withDefault]
|
||||
|
||||
import Bool exposing [Bool]
|
||||
|
||||
## The result of an operation that could fail: either the operation went
|
||||
## okay, or else there was an error of some sort.
|
||||
|
|
|
@ -1,37 +1,35 @@
|
|||
interface Set
|
||||
exposes [
|
||||
Set,
|
||||
empty,
|
||||
withCapacity,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
single,
|
||||
walk,
|
||||
walkUntil,
|
||||
keepIf,
|
||||
dropIf,
|
||||
insert,
|
||||
len,
|
||||
isEmpty,
|
||||
capacity,
|
||||
remove,
|
||||
contains,
|
||||
toList,
|
||||
fromList,
|
||||
union,
|
||||
intersection,
|
||||
difference,
|
||||
map,
|
||||
joinMap,
|
||||
]
|
||||
imports [
|
||||
List,
|
||||
Bool.{ Bool, Eq },
|
||||
Dict.{ Dict },
|
||||
Num.{ U64 },
|
||||
Hash.{ Hash, Hasher },
|
||||
Inspect.{ Inspect, Inspector, InspectFormatter },
|
||||
]
|
||||
module [
|
||||
Set,
|
||||
empty,
|
||||
withCapacity,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
single,
|
||||
walk,
|
||||
walkUntil,
|
||||
keepIf,
|
||||
dropIf,
|
||||
insert,
|
||||
len,
|
||||
isEmpty,
|
||||
capacity,
|
||||
remove,
|
||||
contains,
|
||||
toList,
|
||||
fromList,
|
||||
union,
|
||||
intersection,
|
||||
difference,
|
||||
map,
|
||||
joinMap,
|
||||
]
|
||||
|
||||
import List
|
||||
import Bool exposing [Bool, Eq]
|
||||
import Dict
|
||||
import Num exposing [U64]
|
||||
import Hash exposing [Hash, Hasher]
|
||||
import Inspect exposing [Inspect, Inspector, InspectFormatter]
|
||||
|
||||
## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type))
|
||||
## type which stores a collection of unique values, without any ordering
|
||||
|
|
|
@ -326,55 +326,53 @@
|
|||
## If a situation like this comes up, a slice can be turned into a separate string by using [`Str.concat`](https://www.roc-lang.org/builtins/Str#concat) to concatenate the slice onto an empty string (or one created with [`Str.withCapacity`](https://www.roc-lang.org/builtins/Str#withCapacity)).
|
||||
##
|
||||
## Currently, the only way to get seamless slices of strings is by calling certain `Str` functions which return them. In general, `Str` functions which accept a string and return a subset of that string tend to do this. [`Str.trim`](https://www.roc-lang.org/builtins/Str#trim) is another example of a function which returns a seamless slice.
|
||||
interface Str
|
||||
exposes [
|
||||
Utf8Problem,
|
||||
Utf8ByteProblem,
|
||||
concat,
|
||||
isEmpty,
|
||||
joinWith,
|
||||
split,
|
||||
repeat,
|
||||
countUtf8Bytes,
|
||||
toUtf8,
|
||||
fromUtf8,
|
||||
startsWith,
|
||||
endsWith,
|
||||
trim,
|
||||
trimStart,
|
||||
trimEnd,
|
||||
toDec,
|
||||
toF64,
|
||||
toF32,
|
||||
toU128,
|
||||
toI128,
|
||||
toU64,
|
||||
toI64,
|
||||
toU32,
|
||||
toI32,
|
||||
toU16,
|
||||
toI16,
|
||||
toU8,
|
||||
toI8,
|
||||
replaceEach,
|
||||
replaceFirst,
|
||||
replaceLast,
|
||||
splitFirst,
|
||||
splitLast,
|
||||
walkUtf8,
|
||||
walkUtf8WithIndex,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
withCapacity,
|
||||
withPrefix,
|
||||
contains,
|
||||
]
|
||||
imports [
|
||||
Bool.{ Bool, Eq },
|
||||
Result.{ Result },
|
||||
List,
|
||||
Num.{ Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec },
|
||||
]
|
||||
module [
|
||||
Utf8Problem,
|
||||
Utf8ByteProblem,
|
||||
concat,
|
||||
isEmpty,
|
||||
joinWith,
|
||||
split,
|
||||
repeat,
|
||||
countUtf8Bytes,
|
||||
toUtf8,
|
||||
fromUtf8,
|
||||
startsWith,
|
||||
endsWith,
|
||||
trim,
|
||||
trimStart,
|
||||
trimEnd,
|
||||
toDec,
|
||||
toF64,
|
||||
toF32,
|
||||
toU128,
|
||||
toI128,
|
||||
toU64,
|
||||
toI64,
|
||||
toU32,
|
||||
toI32,
|
||||
toU16,
|
||||
toI16,
|
||||
toU8,
|
||||
toI8,
|
||||
replaceEach,
|
||||
replaceFirst,
|
||||
replaceLast,
|
||||
splitFirst,
|
||||
splitLast,
|
||||
walkUtf8,
|
||||
walkUtf8WithIndex,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
withCapacity,
|
||||
withPrefix,
|
||||
contains,
|
||||
]
|
||||
|
||||
import Bool exposing [Bool]
|
||||
import Result exposing [Result]
|
||||
import List
|
||||
import Num exposing [Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec]
|
||||
|
||||
Utf8ByteProblem : [
|
||||
InvalidStartByte,
|
||||
|
|
|
@ -1,44 +1,18 @@
|
|||
## THIS MODULE IS DEPRECATED AND CURRENTLY IN THE PROCESS OF BEING REMOVED
|
||||
## FROM STD LIBRARY
|
||||
interface TotallyNotJson
|
||||
exposes [
|
||||
Json,
|
||||
json,
|
||||
jsonWithOptions,
|
||||
]
|
||||
imports [
|
||||
List,
|
||||
Str,
|
||||
Result.{ Result },
|
||||
Encode,
|
||||
Encode.{
|
||||
Encoder,
|
||||
EncoderFormatting,
|
||||
appendWith,
|
||||
},
|
||||
Decode,
|
||||
Decode.{
|
||||
DecoderFormatting,
|
||||
DecodeResult,
|
||||
},
|
||||
Num.{
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
F32,
|
||||
F64,
|
||||
Dec,
|
||||
},
|
||||
Bool.{ Bool, Eq },
|
||||
Result,
|
||||
]
|
||||
module [
|
||||
Json,
|
||||
json,
|
||||
jsonWithOptions,
|
||||
]
|
||||
|
||||
import List
|
||||
import Str
|
||||
import Result
|
||||
import Encode exposing [EncoderFormatting, appendWith]
|
||||
import Decode exposing [DecoderFormatting, DecodeResult]
|
||||
import Num exposing [U8, U16, U64, F32, F64, Dec]
|
||||
import Bool exposing [Bool]
|
||||
|
||||
## An opaque type with the `EncoderFormatting` and
|
||||
## `DecoderFormatting` abilities.
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
package "builtins"
|
||||
exposes [Str, Num, Bool, Result, List, Dict, Set, Decode, Encode, Hash, Box, TotallyNotJson, Inspect]
|
||||
packages {}
|
||||
package [
|
||||
Str,
|
||||
Num,
|
||||
Bool,
|
||||
Result,
|
||||
List,
|
||||
Dict,
|
||||
Set,
|
||||
Decode,
|
||||
Encode,
|
||||
Hash,
|
||||
Box,
|
||||
TotallyNotJson,
|
||||
Inspect,
|
||||
] {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::env::Env;
|
||||
use crate::procedure::References;
|
||||
use crate::procedure::{QualifiedReference, References};
|
||||
use crate::scope::{PendingAbilitiesInScope, Scope};
|
||||
use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
|
@ -17,7 +17,7 @@ use roc_types::types::{
|
|||
pub struct Annotation {
|
||||
pub typ: Type,
|
||||
pub introduced_variables: IntroducedVariables,
|
||||
pub references: VecSet<Symbol>,
|
||||
pub references: References,
|
||||
pub aliases: VecMap<Symbol, Alias>,
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,7 @@ impl Annotation {
|
|||
references: &mut References,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
) {
|
||||
for symbol in self.references.iter() {
|
||||
references.insert_type_lookup(*symbol);
|
||||
}
|
||||
references.union_mut(&self.references);
|
||||
|
||||
introduced_variables.union(&self.introduced_variables);
|
||||
|
||||
|
@ -291,7 +289,7 @@ pub(crate) fn canonicalize_annotation(
|
|||
annotation_for: AnnotationFor,
|
||||
) -> Annotation {
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
let mut references = VecSet::default();
|
||||
let mut references = References::new();
|
||||
let mut aliases = VecMap::default();
|
||||
|
||||
let (annotation, region) = match annotation {
|
||||
|
@ -381,13 +379,17 @@ pub(crate) fn make_apply_symbol(
|
|||
scope: &mut Scope,
|
||||
module_name: &str,
|
||||
ident: &str,
|
||||
references: &mut References,
|
||||
) -> Result<Symbol, Type> {
|
||||
if module_name.is_empty() {
|
||||
// Since module_name was empty, this is an unqualified type.
|
||||
// Look it up in scope!
|
||||
|
||||
match scope.lookup_str(ident, region) {
|
||||
Ok(symbol) => Ok(symbol),
|
||||
Ok(symbol) => {
|
||||
references.insert_type_lookup(symbol, QualifiedReference::Unqualified);
|
||||
Ok(symbol)
|
||||
}
|
||||
Err(problem) => {
|
||||
env.problem(roc_problem::can::Problem::RuntimeError(problem));
|
||||
|
||||
|
@ -396,7 +398,10 @@ pub(crate) fn make_apply_symbol(
|
|||
}
|
||||
} else {
|
||||
match env.qualified_lookup(scope, module_name, ident, region) {
|
||||
Ok(symbol) => Ok(symbol),
|
||||
Ok(symbol) => {
|
||||
references.insert_type_lookup(symbol, QualifiedReference::Qualified);
|
||||
Ok(symbol)
|
||||
}
|
||||
Err(problem) => {
|
||||
// Either the module wasn't imported, or
|
||||
// it was imported but it doesn't expose this ident.
|
||||
|
@ -537,7 +542,7 @@ fn can_annotation_help(
|
|||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
references: &mut References,
|
||||
) -> Type {
|
||||
use roc_parse::ast::TypeAnnotation::*;
|
||||
|
||||
|
@ -580,15 +585,14 @@ fn can_annotation_help(
|
|||
Type::Function(args, Box::new(closure), Box::new(ret))
|
||||
}
|
||||
Apply(module_name, ident, type_arguments) => {
|
||||
let symbol = match make_apply_symbol(env, region, scope, module_name, ident) {
|
||||
let symbol = match make_apply_symbol(env, region, scope, module_name, ident, references)
|
||||
{
|
||||
Err(problem) => return problem,
|
||||
Ok(symbol) => symbol,
|
||||
};
|
||||
|
||||
let mut args = Vec::new();
|
||||
|
||||
references.insert(symbol);
|
||||
|
||||
if scope.abilities_store.is_ability(symbol) {
|
||||
let fresh_ty_var = find_fresh_var_name(introduced_variables);
|
||||
|
||||
|
@ -744,7 +748,7 @@ fn can_annotation_help(
|
|||
let mut vars = Vec::with_capacity(loc_vars.len());
|
||||
let mut lowercase_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(loc_vars.len());
|
||||
|
||||
references.insert(symbol);
|
||||
references.insert_type_lookup(symbol, QualifiedReference::Unqualified);
|
||||
|
||||
for loc_var in *loc_vars {
|
||||
let var = match loc_var.value {
|
||||
|
@ -1057,7 +1061,7 @@ fn canonicalize_has_clause(
|
|||
introduced_variables: &mut IntroducedVariables,
|
||||
clause: &Loc<roc_parse::ast::ImplementsClause<'_>>,
|
||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||
references: &mut VecSet<Symbol>,
|
||||
references: &mut References,
|
||||
) -> Result<(), Type> {
|
||||
let Loc {
|
||||
region,
|
||||
|
@ -1080,7 +1084,7 @@ fn canonicalize_has_clause(
|
|||
{
|
||||
let ability = match ability {
|
||||
TypeAnnotation::Apply(module_name, ident, _type_arguments) => {
|
||||
let symbol = make_apply_symbol(env, region, scope, module_name, ident)?;
|
||||
let symbol = make_apply_symbol(env, region, scope, module_name, ident, references)?;
|
||||
|
||||
// Ability defined locally, whose members we are constructing right now...
|
||||
if !pending_abilities_in_scope.contains_key(&symbol)
|
||||
|
@ -1098,7 +1102,6 @@ fn canonicalize_has_clause(
|
|||
}
|
||||
};
|
||||
|
||||
references.insert(ability);
|
||||
let already_seen = can_abilities.insert(ability);
|
||||
|
||||
if already_seen {
|
||||
|
@ -1132,7 +1135,7 @@ fn can_extension_type(
|
|||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
references: &mut References,
|
||||
opt_ext: &Option<&Loc<TypeAnnotation>>,
|
||||
ext_problem_kind: roc_problem::can::ExtensionTypeKind,
|
||||
) -> (Type, ExtImplicitOpenness) {
|
||||
|
@ -1335,7 +1338,7 @@ fn can_assigned_fields<'a>(
|
|||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
references: &mut References,
|
||||
) -> SendMap<Lowercase, RecordField<Type>> {
|
||||
use roc_parse::ast::AssignedField::*;
|
||||
use roc_types::types::RecordField::*;
|
||||
|
@ -1450,7 +1453,7 @@ fn can_assigned_tuple_elems(
|
|||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
references: &mut References,
|
||||
) -> VecMap<usize, Type> {
|
||||
let mut elem_types = VecMap::with_capacity(elems.len());
|
||||
|
||||
|
@ -1484,7 +1487,7 @@ fn can_tags<'a>(
|
|||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
local_aliases: &mut VecMap<Symbol, Alias>,
|
||||
references: &mut VecSet<Symbol>,
|
||||
references: &mut References,
|
||||
) -> Vec<(TagName, Vec<Type>)> {
|
||||
let mut tag_types = Vec::with_capacity(tags.len());
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ use roc_collections::{ImSet, MutMap, SendMap};
|
|||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::ident::QualifiedModuleName;
|
||||
use roc_module::symbol::IdentId;
|
||||
use roc_module::symbol::ModuleId;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -52,6 +54,10 @@ use roc_types::types::MemberImpl;
|
|||
use roc_types::types::OptAbleType;
|
||||
use roc_types::types::{Alias, Type};
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Def {
|
||||
|
@ -158,6 +164,12 @@ enum PendingValueDef<'a> {
|
|||
&'a Loc<ast::TypeAnnotation<'a>>,
|
||||
&'a Loc<ast::Expr<'a>>,
|
||||
),
|
||||
/// Ingested file
|
||||
IngestedFile(
|
||||
Loc<Pattern>,
|
||||
Loc<ast::TypeAnnotation<'a>>,
|
||||
Loc<ast::StrLiteral<'a>>,
|
||||
),
|
||||
}
|
||||
|
||||
impl PendingValueDef<'_> {
|
||||
|
@ -166,6 +178,7 @@ impl PendingValueDef<'_> {
|
|||
PendingValueDef::AnnotationOnly(_, loc_pattern, _) => loc_pattern,
|
||||
PendingValueDef::Body(loc_pattern, _) => loc_pattern,
|
||||
PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern,
|
||||
PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,9 +370,7 @@ fn canonicalize_alias<'a>(
|
|||
);
|
||||
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
for symbol in can_ann.references {
|
||||
output.references.insert_type_lookup(symbol);
|
||||
}
|
||||
output.references.union_mut(&can_ann.references);
|
||||
|
||||
let mut can_vars: Vec<Loc<AliasVar>> = Vec::with_capacity(vars.len());
|
||||
let mut is_phantom = false;
|
||||
|
@ -495,7 +506,7 @@ fn canonicalize_claimed_ability_impl<'a>(
|
|||
// OPTION-1: The implementation identifier is the only identifier of that name in the
|
||||
// scope. For example,
|
||||
//
|
||||
// interface F imports [] exposes []
|
||||
// module []
|
||||
//
|
||||
// Hello := {} implements [Encoding.{ toEncoder }]
|
||||
//
|
||||
|
@ -507,7 +518,9 @@ fn canonicalize_claimed_ability_impl<'a>(
|
|||
// OPTION-2: The implementation identifier is a unique shadow of the ability member,
|
||||
// which has also been explicitly imported. For example,
|
||||
//
|
||||
// interface F imports [Encoding.{ toEncoder }] exposes []
|
||||
// module []
|
||||
//
|
||||
// import Encoding exposing [toEncoder]
|
||||
//
|
||||
// Hello := {} implements [Encoding.{ toEncoder }]
|
||||
//
|
||||
|
@ -703,6 +716,8 @@ fn canonicalize_opaque<'a>(
|
|||
AliasKind::Opaque,
|
||||
)?;
|
||||
|
||||
let mut references = References::new();
|
||||
|
||||
let mut derived_defs = Vec::new();
|
||||
if let Some(has_abilities) = has_abilities {
|
||||
let has_abilities = has_abilities.value.collection();
|
||||
|
@ -721,7 +736,8 @@ fn canonicalize_opaque<'a>(
|
|||
// Op := {} has [Eq]
|
||||
let (ability, members) = match ability.value {
|
||||
ast::TypeAnnotation::Apply(module_name, ident, []) => {
|
||||
match make_apply_symbol(env, region, scope, module_name, ident) {
|
||||
match make_apply_symbol(env, region, scope, module_name, ident, &mut references)
|
||||
{
|
||||
Ok(ability) => {
|
||||
let opt_members = scope
|
||||
.abilities_store
|
||||
|
@ -914,6 +930,8 @@ fn canonicalize_opaque<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
output.references.union_mut(&references);
|
||||
|
||||
Ok(CanonicalizedOpaque {
|
||||
opaque_def: alias,
|
||||
derived_defs,
|
||||
|
@ -928,7 +946,12 @@ pub(crate) fn canonicalize_defs<'a>(
|
|||
scope: &mut Scope,
|
||||
loc_defs: &'a mut roc_parse::ast::Defs<'a>,
|
||||
pattern_type: PatternType,
|
||||
) -> (CanDefs, Output, MutMap<Symbol, Region>) {
|
||||
) -> (
|
||||
CanDefs,
|
||||
Output,
|
||||
MutMap<Symbol, Region>,
|
||||
Vec<IntroducedImport>,
|
||||
) {
|
||||
// Canonicalizing defs while detecting shadowing involves a multi-step process:
|
||||
//
|
||||
// 1. Go through each of the patterns.
|
||||
|
@ -955,7 +978,7 @@ pub(crate) fn canonicalize_defs<'a>(
|
|||
// there are opaques that implement an ability using a value symbol). But, value symbols might
|
||||
// shadow symbols defined in a local ability def.
|
||||
|
||||
for (_, either_index) in loc_defs.tags.iter().enumerate() {
|
||||
for either_index in loc_defs.tags.iter() {
|
||||
if let Ok(type_index) = either_index.split() {
|
||||
let type_def = &loc_defs.type_defs[type_index.index()];
|
||||
let pending_type_def = to_pending_type_def(env, type_def, scope, pattern_type);
|
||||
|
@ -978,6 +1001,7 @@ pub(crate) fn canonicalize_defs<'a>(
|
|||
env,
|
||||
var_store,
|
||||
value_def,
|
||||
region,
|
||||
scope,
|
||||
&pending_abilities_in_scope,
|
||||
&mut output,
|
||||
|
@ -1034,7 +1058,12 @@ fn canonicalize_value_defs<'a>(
|
|||
pattern_type: PatternType,
|
||||
mut aliases: VecMap<Symbol, Alias>,
|
||||
mut symbols_introduced: MutMap<Symbol, Region>,
|
||||
) -> (CanDefs, Output, MutMap<Symbol, Region>) {
|
||||
) -> (
|
||||
CanDefs,
|
||||
Output,
|
||||
MutMap<Symbol, Region>,
|
||||
Vec<IntroducedImport>,
|
||||
) {
|
||||
// Canonicalize all the patterns, record shadowing problems, and store
|
||||
// the ast::Expr values in pending_exprs for further canonicalization
|
||||
// once we've finished assembling the entire scope.
|
||||
|
@ -1043,6 +1072,8 @@ fn canonicalize_value_defs<'a>(
|
|||
let mut pending_expects = Vec::with_capacity(value_defs.len());
|
||||
let mut pending_expect_fx = Vec::with_capacity(value_defs.len());
|
||||
|
||||
let mut imports_introduced = Vec::with_capacity(value_defs.len());
|
||||
|
||||
for loc_pending_def in value_defs {
|
||||
match loc_pending_def.value {
|
||||
PendingValue::Def(pending_def) => {
|
||||
|
@ -1062,6 +1093,11 @@ fn canonicalize_value_defs<'a>(
|
|||
PendingValue::ExpectFx(pending_expect) => {
|
||||
pending_expect_fx.push(pending_expect);
|
||||
}
|
||||
PendingValue::ModuleImport(introduced_import) => {
|
||||
imports_introduced.push(introduced_import);
|
||||
}
|
||||
PendingValue::InvalidIngestedFile => { /* skip */ }
|
||||
PendingValue::ImportNameConflict => { /* skip */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1171,7 +1207,7 @@ fn canonicalize_value_defs<'a>(
|
|||
aliases,
|
||||
};
|
||||
|
||||
(can_defs, output, symbols_introduced)
|
||||
(can_defs, output, symbols_introduced, imports_introduced)
|
||||
}
|
||||
|
||||
struct CanonicalizedTypeDefs<'a> {
|
||||
|
@ -1385,9 +1421,7 @@ fn resolve_abilities(
|
|||
);
|
||||
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
for symbol in member_annot.references {
|
||||
output.references.insert_type_lookup(symbol);
|
||||
}
|
||||
output.references.union_mut(&member_annot.references);
|
||||
|
||||
// What variables in the annotation are bound to the parent ability, and what variables
|
||||
// are bound to some other ability?
|
||||
|
@ -2302,6 +2336,69 @@ fn canonicalize_pending_value_def<'a>(
|
|||
None,
|
||||
)
|
||||
}
|
||||
IngestedFile(loc_pattern, loc_ann, path_literal) => {
|
||||
let relative_path =
|
||||
if let ast::StrLiteral::PlainLine(ingested_path) = path_literal.value {
|
||||
ingested_path
|
||||
} else {
|
||||
todo!(
|
||||
"Only plain strings are supported. Other cases should be made impossible here"
|
||||
);
|
||||
};
|
||||
|
||||
let mut file_path: PathBuf = env.module_path.into();
|
||||
// Remove the header file name and push the new path.
|
||||
file_path.pop();
|
||||
file_path.push(relative_path);
|
||||
|
||||
let mut bytes = vec![];
|
||||
|
||||
let expr = match fs::File::open(&file_path)
|
||||
.and_then(|mut file| file.read_to_end(&mut bytes))
|
||||
{
|
||||
Ok(_) => Expr::IngestedFile(file_path.into(), Arc::new(bytes), var_store.fresh()),
|
||||
Err(e) => {
|
||||
env.problems.push(Problem::FileProblem {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
});
|
||||
|
||||
Expr::RuntimeError(RuntimeError::ReadIngestedFileError {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
region: path_literal.region,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let loc_expr = Loc::at(path_literal.region, expr);
|
||||
|
||||
let can_ann = canonicalize_annotation(
|
||||
env,
|
||||
scope,
|
||||
&loc_ann.value,
|
||||
loc_ann.region,
|
||||
var_store,
|
||||
pending_abilities_in_scope,
|
||||
AnnotationFor::Value,
|
||||
);
|
||||
|
||||
output.references.union_mut(&can_ann.references);
|
||||
|
||||
let def = single_can_def(
|
||||
loc_pattern,
|
||||
loc_expr,
|
||||
var_store.fresh(),
|
||||
Some(Loc::at(loc_ann.region, can_ann)),
|
||||
SendMap::default(),
|
||||
);
|
||||
|
||||
DefOutput {
|
||||
output,
|
||||
references: DefReferences::Value(References::new()),
|
||||
def,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Disallow ability specializations that aren't on the toplevel (note: we might loosen this
|
||||
|
@ -2460,7 +2557,7 @@ pub fn can_defs_with_return<'a>(
|
|||
loc_defs: &'a mut Defs<'a>,
|
||||
loc_ret: &'a Loc<ast::Expr<'a>>,
|
||||
) -> (Expr, Output) {
|
||||
let (unsorted, defs_output, symbols_introduced) = canonicalize_defs(
|
||||
let (unsorted, defs_output, symbols_introduced, imports_introduced) = canonicalize_defs(
|
||||
env,
|
||||
Output::default(),
|
||||
var_store,
|
||||
|
@ -2494,6 +2591,8 @@ pub fn can_defs_with_return<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
report_unused_imports(imports_introduced, &output.references, env, scope);
|
||||
|
||||
let mut loc_expr: Loc<Expr> = ret_expr;
|
||||
|
||||
for declaration in declarations.into_iter().rev() {
|
||||
|
@ -2503,6 +2602,28 @@ pub fn can_defs_with_return<'a>(
|
|||
(loc_expr.value, output)
|
||||
}
|
||||
|
||||
pub fn report_unused_imports(
|
||||
imports_introduced: Vec<IntroducedImport>,
|
||||
references: &References,
|
||||
env: &mut Env<'_>,
|
||||
scope: &mut Scope,
|
||||
) {
|
||||
for import in imports_introduced {
|
||||
if references.has_module_lookup(import.module_id) {
|
||||
for (symbol, region) in &import.exposed_symbols {
|
||||
if !references.has_unqualified_type_or_value_lookup(*symbol)
|
||||
&& !scope.abilities_store.is_specialization_name(*symbol)
|
||||
&& !import.is_task(env)
|
||||
{
|
||||
env.problem(Problem::UnusedImport(*symbol, *region));
|
||||
}
|
||||
}
|
||||
} else if !import.is_task(env) {
|
||||
env.problem(Problem::UnusedModuleImport(import.module_id, import.region));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
|
||||
match decl {
|
||||
Declaration::Declare(def) => {
|
||||
|
@ -2750,7 +2871,10 @@ enum PendingValue<'a> {
|
|||
Dbg(PendingExpectOrDbg<'a>),
|
||||
Expect(PendingExpectOrDbg<'a>),
|
||||
ExpectFx(PendingExpectOrDbg<'a>),
|
||||
ModuleImport(IntroducedImport),
|
||||
SignatureDefMismatch,
|
||||
InvalidIngestedFile,
|
||||
ImportNameConflict,
|
||||
}
|
||||
|
||||
struct PendingExpectOrDbg<'a> {
|
||||
|
@ -2758,10 +2882,28 @@ struct PendingExpectOrDbg<'a> {
|
|||
preceding_comment: Region,
|
||||
}
|
||||
|
||||
pub struct IntroducedImport {
|
||||
module_id: ModuleId,
|
||||
region: Region,
|
||||
exposed_symbols: Vec<(Symbol, Region)>,
|
||||
}
|
||||
|
||||
impl IntroducedImport {
|
||||
pub fn is_task(&self, env: &Env<'_>) -> bool {
|
||||
// Temporarily needed for `!` convenience. Can be removed when Task becomes a builtin.
|
||||
match env.qualified_module_ids.get_name(self.module_id) {
|
||||
Some(name) => name.as_inner().as_str() == "Task",
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn to_pending_value_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
var_store: &mut VarStore,
|
||||
def: &'a ast::ValueDef<'a>,
|
||||
region: Region,
|
||||
scope: &mut Scope,
|
||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||
output: &mut Output,
|
||||
|
@ -2875,6 +3017,116 @@ fn to_pending_value_def<'a>(
|
|||
preceding_comment: *preceding_comment,
|
||||
}),
|
||||
|
||||
ModuleImport(module_import) => {
|
||||
let qualified_module_name: QualifiedModuleName = module_import.name.value.into();
|
||||
let module_name = qualified_module_name.module.clone();
|
||||
let pq_module_name = qualified_module_name.into_pq_module_name(env.opt_shorthand);
|
||||
|
||||
let module_id = env
|
||||
.qualified_module_ids
|
||||
.get_id(&pq_module_name)
|
||||
.expect("Module id should have been added in load");
|
||||
|
||||
let name_with_alias = match module_import.alias {
|
||||
Some(alias) => ModuleName::from(alias.item.value.as_str()),
|
||||
None => module_name.clone(),
|
||||
};
|
||||
|
||||
if let Err(existing_import) =
|
||||
scope
|
||||
.modules
|
||||
.insert(name_with_alias.clone(), module_id, region)
|
||||
{
|
||||
env.problems.push(Problem::ImportNameConflict {
|
||||
name: name_with_alias,
|
||||
is_alias: module_import.alias.is_some(),
|
||||
new_module_id: module_id,
|
||||
new_import_region: region,
|
||||
existing_import,
|
||||
});
|
||||
|
||||
return PendingValue::ImportNameConflict;
|
||||
}
|
||||
|
||||
let exposed_names = module_import
|
||||
.exposed
|
||||
.map(|kw| kw.item.items)
|
||||
.unwrap_or_default();
|
||||
|
||||
if exposed_names.is_empty() && !env.home.is_builtin() && module_id.is_automatically_imported() {
|
||||
env.problems.push(Problem::ExplicitBuiltinImport(module_id, region));
|
||||
}
|
||||
|
||||
let exposed_ids = env
|
||||
.dep_idents
|
||||
.get(&module_id)
|
||||
.expect("Module id should have been added in load");
|
||||
|
||||
let mut exposed_symbols = Vec::with_capacity(exposed_names.len());
|
||||
|
||||
for loc_name in exposed_names {
|
||||
let exposed_name = loc_name.value.item();
|
||||
let name = exposed_name.as_str();
|
||||
let ident = Ident::from(name);
|
||||
|
||||
match exposed_ids.get_id(name) {
|
||||
Some(ident_id) => {
|
||||
let symbol = Symbol::new(module_id, ident_id);
|
||||
exposed_symbols.push((symbol, loc_name.region));
|
||||
|
||||
if let Err((_shadowed_symbol, existing_symbol_region)) = scope.import_symbol(ident, symbol, loc_name.region) {
|
||||
if symbol.is_automatically_imported() {
|
||||
env.problem(Problem::ExplicitBuiltinTypeImport(
|
||||
symbol,
|
||||
loc_name.region,
|
||||
));
|
||||
} else {
|
||||
env.problem(Problem::ImportShadowsSymbol {
|
||||
region: loc_name.region,
|
||||
new_symbol: symbol,
|
||||
existing_symbol_region,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
env.problem(Problem::RuntimeError(RuntimeError::ValueNotExposed {
|
||||
module_name: module_name.clone(),
|
||||
ident,
|
||||
region: loc_name.region,
|
||||
exposed_values: exposed_ids.exposed_values(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PendingValue::ModuleImport(IntroducedImport {
|
||||
module_id,
|
||||
region,
|
||||
exposed_symbols,
|
||||
})
|
||||
}
|
||||
IngestedFileImport(ingested_file) => {
|
||||
let typed_ident = ingested_file.name.item.extract_spaces().item;
|
||||
|
||||
let symbol = match scope.introduce(typed_ident.ident.value.into(), typed_ident.ident.region) {
|
||||
Ok(symbol ) => symbol,
|
||||
Err((original, shadow, _)) => {
|
||||
env.problem(Problem::Shadowing {
|
||||
original_region: original.region,
|
||||
shadow,
|
||||
kind: ShadowKind::Variable
|
||||
});
|
||||
|
||||
return PendingValue::InvalidIngestedFile;
|
||||
}
|
||||
};
|
||||
|
||||
let loc_pattern = Loc::at(typed_ident.ident.region, Pattern::Identifier(symbol));
|
||||
|
||||
PendingValue::Def(PendingValueDef::IngestedFile(loc_pattern, typed_ident.ann, ingested_file.path))
|
||||
}
|
||||
Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,6 +130,13 @@ fn desugar_value_def<'a>(
|
|||
preceding_comment: *preceding_comment,
|
||||
}
|
||||
}
|
||||
ModuleImport(roc_parse::ast::ModuleImport {
|
||||
before_name: _,
|
||||
name: _,
|
||||
alias: _,
|
||||
exposed: _,
|
||||
}) => *def,
|
||||
IngestedFileImport(_) => *def,
|
||||
|
||||
Stmt(stmt_expr) => {
|
||||
// desugar into a Body({}, stmt_expr)
|
||||
|
@ -247,6 +254,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
|||
|
||||
// TODO support desugaring of Dbg, Expect, and ExpectFx
|
||||
Dbg { .. } | Expect { .. } | ExpectFx { .. } => value_def,
|
||||
ModuleImport { .. } | IngestedFileImport(_) => value_def,
|
||||
|
||||
Stmt(..) => {
|
||||
internal_error!(
|
||||
|
@ -281,7 +289,6 @@ pub fn desugar_expr<'a>(
|
|||
| UnappliedRecordBuilder { .. }
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| IngestedFile(_, _)
|
||||
| Crash => loc_expr,
|
||||
|
||||
Str(str_literal) => match str_literal {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::procedure::References;
|
||||
use crate::scope::Scope;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::{MutMap, VecSet};
|
||||
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||
use roc_module::symbol::{IdentIdsByModule, ModuleId, ModuleIds, Symbol};
|
||||
use roc_module::ident::{Ident, ModuleName};
|
||||
use roc_module::symbol::{IdentIdsByModule, ModuleId, PQModuleName, PackageModuleIds, Symbol};
|
||||
use roc_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
||||
|
@ -13,9 +15,11 @@ pub struct Env<'a> {
|
|||
/// are assumed to be relative to this path.
|
||||
pub home: ModuleId,
|
||||
|
||||
pub module_path: &'a Path,
|
||||
|
||||
pub dep_idents: &'a IdentIdsByModule,
|
||||
|
||||
pub module_ids: &'a ModuleIds,
|
||||
pub qualified_module_ids: &'a PackageModuleIds<'a>,
|
||||
|
||||
/// Problems we've encountered along the way, which will be reported to the user at the end.
|
||||
pub problems: Vec<Problem>,
|
||||
|
@ -35,26 +39,32 @@ pub struct Env<'a> {
|
|||
pub top_level_symbols: VecSet<Symbol>,
|
||||
|
||||
pub arena: &'a Bump,
|
||||
|
||||
pub opt_shorthand: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Env<'a> {
|
||||
pub fn new(
|
||||
arena: &'a Bump,
|
||||
home: ModuleId,
|
||||
module_path: &'a Path,
|
||||
dep_idents: &'a IdentIdsByModule,
|
||||
module_ids: &'a ModuleIds,
|
||||
qualified_module_ids: &'a PackageModuleIds<'a>,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
) -> Env<'a> {
|
||||
Env {
|
||||
arena,
|
||||
home,
|
||||
module_path,
|
||||
dep_idents,
|
||||
module_ids,
|
||||
qualified_module_ids,
|
||||
problems: Vec::new(),
|
||||
closures: MutMap::default(),
|
||||
qualified_value_lookups: VecSet::default(),
|
||||
qualified_type_lookups: VecSet::default(),
|
||||
tailcallable_symbol: None,
|
||||
top_level_symbols: VecSet::default(),
|
||||
opt_shorthand,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,17 +82,20 @@ impl<'a> Env<'a> {
|
|||
|
||||
let module_name = ModuleName::from(module_name_str);
|
||||
|
||||
match self.module_ids.get_id(&module_name) {
|
||||
match scope.modules.get_id(&module_name) {
|
||||
Some(module_id) => self.qualified_lookup_help(scope, module_id, ident, region),
|
||||
None => Err(RuntimeError::ModuleNotImported {
|
||||
module_name,
|
||||
imported_modules: self
|
||||
.module_ids
|
||||
.available_modules()
|
||||
module_name: module_name.clone(),
|
||||
imported_modules: scope
|
||||
.modules
|
||||
.available_names()
|
||||
.map(|string| string.as_ref().into())
|
||||
.collect(),
|
||||
region,
|
||||
module_exists: false,
|
||||
module_exists: self
|
||||
.qualified_module_ids
|
||||
.get_id(&PQModuleName::Unqualified(module_name))
|
||||
.is_some(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +107,11 @@ impl<'a> Env<'a> {
|
|||
ident: &str,
|
||||
region: Region,
|
||||
) -> Result<Symbol, RuntimeError> {
|
||||
self.qualified_lookup_help(scope, module_id, ident, region)
|
||||
if !scope.modules.has_id(module_id) {
|
||||
Err(self.module_exists_but_not_imported(scope, module_id, region))
|
||||
} else {
|
||||
self.qualified_lookup_help(scope, module_id, ident, region)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Err if the symbol resolved, but it was not exposed by the given module
|
||||
|
@ -153,43 +170,46 @@ impl<'a> Env<'a> {
|
|||
|
||||
Ok(symbol)
|
||||
}
|
||||
None => {
|
||||
let exposed_values = exposed_ids
|
||||
.ident_strs()
|
||||
.filter(|(_, ident)| ident.starts_with(|c: char| c.is_lowercase()))
|
||||
.map(|(_, ident)| Lowercase::from(ident))
|
||||
.collect();
|
||||
Err(RuntimeError::ValueNotExposed {
|
||||
module_name: self
|
||||
.module_ids
|
||||
.get_name(module_id)
|
||||
.expect("Module ID known, but not in the module IDs somehow")
|
||||
.clone(),
|
||||
ident: Ident::from(ident),
|
||||
region,
|
||||
exposed_values,
|
||||
})
|
||||
}
|
||||
None => Err(RuntimeError::ValueNotExposed {
|
||||
module_name: self
|
||||
.qualified_module_ids
|
||||
.get_name(module_id)
|
||||
.expect("Module ID known, but not in the module IDs somehow")
|
||||
.as_inner()
|
||||
.clone(),
|
||||
ident: Ident::from(ident),
|
||||
region,
|
||||
exposed_values: exposed_ids.exposed_values(),
|
||||
}),
|
||||
},
|
||||
None => Err(RuntimeError::ModuleNotImported {
|
||||
module_name: self
|
||||
.module_ids
|
||||
.get_name(module_id)
|
||||
.expect("Module ID known, but not in the module IDs somehow")
|
||||
.clone(),
|
||||
imported_modules: self
|
||||
.dep_idents
|
||||
.keys()
|
||||
.filter_map(|module_id| self.module_ids.get_name(*module_id))
|
||||
.map(|module_name| module_name.as_ref().into())
|
||||
.collect(),
|
||||
region,
|
||||
module_exists: true,
|
||||
}),
|
||||
_ => Err(self.module_exists_but_not_imported(scope, module_id, region)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn module_exists_but_not_imported(
|
||||
&self,
|
||||
scope: &Scope,
|
||||
module_id: ModuleId,
|
||||
region: Region,
|
||||
) -> RuntimeError {
|
||||
RuntimeError::ModuleNotImported {
|
||||
module_name: self
|
||||
.qualified_module_ids
|
||||
.get_name(module_id)
|
||||
.expect("Module ID known, but not in the module IDs somehow")
|
||||
.as_inner()
|
||||
.clone(),
|
||||
imported_modules: scope
|
||||
.modules
|
||||
.available_names()
|
||||
.map(|string| string.as_ref().into())
|
||||
.collect(),
|
||||
region,
|
||||
module_exists: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn problem(&mut self, problem: Problem) {
|
||||
self.problems.push(problem)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::num::{
|
|||
int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound,
|
||||
};
|
||||
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows};
|
||||
use crate::procedure::References;
|
||||
use crate::procedure::{QualifiedReference, References};
|
||||
use crate::scope::Scope;
|
||||
use crate::traverse::{walk_expr, Visitor};
|
||||
use roc_collections::soa::Index;
|
||||
|
@ -27,8 +27,6 @@ use roc_types::num::SingleQuoteBound;
|
|||
use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, RedundantMark, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Category, IndexOrField, LambdaSet, OptAbleVar, Type};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::{char, u32};
|
||||
|
@ -742,48 +740,6 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
|
||||
|
||||
ast::Expr::IngestedFile(file_path, _) => match File::open(file_path) {
|
||||
Ok(mut file) => {
|
||||
let mut bytes = vec![];
|
||||
match file.read_to_end(&mut bytes) {
|
||||
Ok(_) => (
|
||||
Expr::IngestedFile(
|
||||
file_path.to_path_buf().into(),
|
||||
Arc::new(bytes),
|
||||
var_store.fresh(),
|
||||
),
|
||||
Output::default(),
|
||||
),
|
||||
Err(e) => {
|
||||
env.problems.push(Problem::FileProblem {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
});
|
||||
|
||||
// This will not manifest as a real runtime error and is just returned to have a value here.
|
||||
// The pushed FileProblem will be fatal to compilation.
|
||||
(
|
||||
Expr::RuntimeError(roc_problem::can::RuntimeError::NoImplementation),
|
||||
Output::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
env.problems.push(Problem::FileProblem {
|
||||
filename: file_path.to_path_buf(),
|
||||
error: e.kind(),
|
||||
});
|
||||
|
||||
// This will not manifest as a real runtime error and is just returned to have a value here.
|
||||
// The pushed FileProblem will be fatal to compilation.
|
||||
(
|
||||
Expr::RuntimeError(roc_problem::can::RuntimeError::NoImplementation),
|
||||
Output::default(),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
ast::Expr::SingleQuote(string) => {
|
||||
let mut it = string.chars().peekable();
|
||||
if let Some(char) = it.next() {
|
||||
|
@ -885,7 +841,9 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
Ok((name, opaque_def)) => {
|
||||
let argument = Box::new(args.pop().unwrap());
|
||||
output.references.insert_type_lookup(name);
|
||||
output
|
||||
.references
|
||||
.insert_type_lookup(name, QualifiedReference::Unqualified);
|
||||
|
||||
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
||||
freshen_opaque_def(var_store, opaque_def);
|
||||
|
@ -1197,7 +1155,9 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
Ok((name, opaque_def)) => {
|
||||
let mut output = Output::default();
|
||||
output.references.insert_type_lookup(name);
|
||||
output
|
||||
.references
|
||||
.insert_type_lookup(name, QualifiedReference::Unqualified);
|
||||
|
||||
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
||||
freshen_opaque_def(var_store, opaque_def);
|
||||
|
@ -1892,7 +1852,9 @@ fn canonicalize_var_lookup(
|
|||
// Look it up in scope!
|
||||
match scope.lookup_str(ident, region) {
|
||||
Ok(symbol) => {
|
||||
output.references.insert_value_lookup(symbol);
|
||||
output
|
||||
.references
|
||||
.insert_value_lookup(symbol, QualifiedReference::Unqualified);
|
||||
|
||||
if scope.abilities_store.is_ability_member_name(symbol) {
|
||||
AbilityMember(
|
||||
|
@ -1915,7 +1877,9 @@ fn canonicalize_var_lookup(
|
|||
// Look it up in the env!
|
||||
match env.qualified_lookup(scope, module_name, ident, region) {
|
||||
Ok(symbol) => {
|
||||
output.references.insert_value_lookup(symbol);
|
||||
output
|
||||
.references
|
||||
.insert_value_lookup(symbol, QualifiedReference::Qualified);
|
||||
|
||||
if scope.abilities_store.is_ability_member_name(symbol) {
|
||||
AbilityMember(
|
||||
|
@ -2426,7 +2390,6 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::Expr::Expect(_, _)
|
||||
| ast::Expr::When(_, _)
|
||||
| ast::Expr::Backpassing(_, _, _)
|
||||
| ast::Expr::IngestedFile(_, _)
|
||||
| ast::Expr::SpaceBefore(_, _)
|
||||
| ast::Expr::Str(StrLiteral::Block(_))
|
||||
| ast::Expr::SpaceAfter(_, _)
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
|
||||
use crate::annotation::{canonicalize_annotation, AnnotationFor};
|
||||
use crate::def::{canonicalize_defs, Def};
|
||||
use crate::def::{canonicalize_defs, report_unused_imports, Def};
|
||||
use crate::effect_module::HostedGeneratedFunctions;
|
||||
use crate::env::Env;
|
||||
use crate::expr::{
|
||||
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
|
||||
};
|
||||
use crate::pattern::{BindingsFromPattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
use crate::scope::Scope;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::{MutMap, SendMap, VecMap, VecSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, ModuleIds, Symbol};
|
||||
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, PackageModuleIds, Symbol};
|
||||
use roc_parse::ast::{Defs, TypeAnnotation};
|
||||
use roc_parse::header::HeaderType;
|
||||
use roc_parse::pattern::PatternType;
|
||||
|
@ -127,7 +130,6 @@ pub struct Module {
|
|||
pub exposed_imports: MutMap<Symbol, Region>,
|
||||
pub exposed_symbols: VecSet<Symbol>,
|
||||
pub referenced_values: VecSet<Symbol>,
|
||||
pub referenced_types: VecSet<Symbol>,
|
||||
/// all aliases. `bool` indicates whether it is exposed
|
||||
pub aliases: MutMap<Symbol, (bool, Alias)>,
|
||||
pub rigid_variables: RigidVariables,
|
||||
|
@ -152,7 +154,6 @@ pub struct ModuleOutput {
|
|||
pub exposed_symbols: VecSet<Symbol>,
|
||||
pub problems: Vec<Problem>,
|
||||
pub referenced_values: VecSet<Symbol>,
|
||||
pub referenced_types: VecSet<Symbol>,
|
||||
pub symbols_from_requires: Vec<(Loc<Symbol>, Loc<Type>)>,
|
||||
pub pending_derives: PendingDerives,
|
||||
pub scope: Scope,
|
||||
|
@ -275,21 +276,38 @@ pub fn canonicalize_module_defs<'a>(
|
|||
loc_defs: &'a mut Defs<'a>,
|
||||
header_type: &roc_parse::header::HeaderType,
|
||||
home: ModuleId,
|
||||
module_path: &str,
|
||||
module_path: &'a str,
|
||||
src: &'a str,
|
||||
module_ids: &'a ModuleIds,
|
||||
qualified_module_ids: &'a PackageModuleIds<'a>,
|
||||
exposed_ident_ids: IdentIds,
|
||||
dep_idents: &'a IdentIdsByModule,
|
||||
aliases: MutMap<Symbol, Alias>,
|
||||
imported_abilities_state: PendingAbilitiesStore,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
initial_scope: MutMap<Ident, (Symbol, Region)>,
|
||||
exposed_symbols: VecSet<Symbol>,
|
||||
symbols_from_requires: &[(Loc<Symbol>, Loc<TypeAnnotation<'a>>)],
|
||||
var_store: &mut VarStore,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
) -> ModuleOutput {
|
||||
let mut can_exposed_imports = MutMap::default();
|
||||
let mut scope = Scope::new(home, exposed_ident_ids, imported_abilities_state);
|
||||
let mut env = Env::new(arena, home, dep_idents, module_ids);
|
||||
let mut scope = Scope::new(
|
||||
home,
|
||||
qualified_module_ids
|
||||
.get_name(home)
|
||||
.expect("home module not found")
|
||||
.as_inner()
|
||||
.to_owned(),
|
||||
exposed_ident_ids,
|
||||
imported_abilities_state,
|
||||
);
|
||||
let mut env = Env::new(
|
||||
arena,
|
||||
home,
|
||||
arena.alloc(Path::new(module_path)),
|
||||
dep_idents,
|
||||
qualified_module_ids,
|
||||
opt_shorthand,
|
||||
);
|
||||
|
||||
for (name, alias) in aliases.into_iter() {
|
||||
scope.add_alias(
|
||||
|
@ -317,26 +335,21 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
let mut rigid_variables = RigidVariables::default();
|
||||
|
||||
// Exposed values are treated like defs that appear before any others, e.g.
|
||||
//
|
||||
// imports [Foo.{ bar, baz }]
|
||||
//
|
||||
// ...is basically the same as if we'd added these extra defs at the start of the module:
|
||||
//
|
||||
// bar = Foo.bar
|
||||
// baz = Foo.baz
|
||||
// Iniital scope values are treated like defs that appear before any others.
|
||||
// They include builtin types that are automatically imported, and for a platform
|
||||
// package, the required values from the app.
|
||||
//
|
||||
// Here we essentially add those "defs" to "the beginning of the module"
|
||||
// by canonicalizing them right before we canonicalize the actual ast::Def nodes.
|
||||
for (ident, (symbol, region)) in exposed_imports {
|
||||
for (ident, (symbol, region)) in initial_scope {
|
||||
let first_char = ident.as_inline_str().as_str().chars().next().unwrap();
|
||||
|
||||
if first_char.is_lowercase() {
|
||||
match scope.import(ident, symbol, region) {
|
||||
match scope.import_symbol(ident, symbol, region) {
|
||||
Ok(()) => {
|
||||
// Add an entry to exposed_imports using the current module's name
|
||||
// as the key; e.g. if this is the Foo module and we have
|
||||
// exposes [Bar.{ baz }] then insert Foo.baz as the key, so when
|
||||
// Bar exposes [baz] then insert Foo.baz as the key, so when
|
||||
// anything references `baz` in this Foo module, it will resolve to Bar.baz.
|
||||
can_exposed_imports.insert(symbol, region);
|
||||
}
|
||||
|
@ -355,7 +368,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
// but now we know this symbol by a different identifier, so we still need to add it to
|
||||
// the scope
|
||||
match scope.import(ident, symbol, region) {
|
||||
match scope.import_symbol(ident, symbol, region) {
|
||||
Ok(()) => {
|
||||
// here we do nothing special
|
||||
}
|
||||
|
@ -369,7 +382,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
let (defs, output, symbols_introduced) = canonicalize_defs(
|
||||
let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs(
|
||||
&mut env,
|
||||
Output::default(),
|
||||
var_store,
|
||||
|
@ -410,18 +423,15 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
|
||||
let mut referenced_values = VecSet::default();
|
||||
let mut referenced_types = VecSet::default();
|
||||
|
||||
// Gather up all the symbols that were referenced across all the defs' lookups.
|
||||
referenced_values.extend(output.references.value_lookups().copied());
|
||||
referenced_types.extend(output.references.type_lookups().copied());
|
||||
|
||||
// Gather up all the symbols that were referenced across all the defs' calls.
|
||||
referenced_values.extend(output.references.calls().copied());
|
||||
|
||||
// Gather up all the symbols that were referenced from other modules.
|
||||
referenced_values.extend(env.qualified_value_lookups.iter().copied());
|
||||
referenced_types.extend(env.qualified_type_lookups.iter().copied());
|
||||
|
||||
// NOTE previously we inserted builtin defs into the list of defs here
|
||||
// this is now done later, in file.rs.
|
||||
|
@ -433,6 +443,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
let new_output = Output {
|
||||
aliases: output.aliases,
|
||||
references: output.references,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -482,6 +493,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
})
|
||||
.collect();
|
||||
|
||||
report_unused_imports(imports_introduced, &output.references, &mut env, &mut scope);
|
||||
|
||||
if let GeneratedInfo::Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
|
@ -545,7 +558,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
let annotation = crate::annotation::Annotation {
|
||||
typ: def_annotation.signature,
|
||||
introduced_variables: def_annotation.introduced_variables,
|
||||
references: Default::default(),
|
||||
references: References::new(),
|
||||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
|
@ -603,7 +616,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
let annotation = crate::annotation::Annotation {
|
||||
typ: def_annotation.signature,
|
||||
introduced_variables: def_annotation.introduced_variables,
|
||||
references: Default::default(),
|
||||
references: References::new(),
|
||||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
|
@ -700,14 +713,12 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
// Incorporate any remaining output.lookups entries into references.
|
||||
referenced_values.extend(output.references.value_lookups().copied());
|
||||
referenced_types.extend(output.references.type_lookups().copied());
|
||||
|
||||
// Incorporate any remaining output.calls entries into references.
|
||||
referenced_values.extend(output.references.calls().copied());
|
||||
|
||||
// Gather up all the symbols that were referenced from other modules.
|
||||
referenced_values.extend(env.qualified_value_lookups.iter().copied());
|
||||
referenced_types.extend(env.qualified_type_lookups.iter().copied());
|
||||
|
||||
let mut fix_closures_no_capture_symbols = VecSet::default();
|
||||
let mut fix_closures_closure_captures = VecMap::default();
|
||||
|
@ -803,7 +814,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
rigid_variables,
|
||||
declarations,
|
||||
referenced_values,
|
||||
referenced_types,
|
||||
exposed_imports: can_exposed_imports,
|
||||
problems: env.problems,
|
||||
symbols_from_requires,
|
||||
|
|
|
@ -446,7 +446,10 @@ pub fn canonicalize_pattern<'a>(
|
|||
let (type_arguments, lambda_set_variables, specialized_def_type) =
|
||||
freshen_opaque_def(var_store, opaque_def);
|
||||
|
||||
output.references.insert_type_lookup(opaque);
|
||||
output.references.insert_type_lookup(
|
||||
opaque,
|
||||
crate::procedure::QualifiedReference::Unqualified,
|
||||
);
|
||||
|
||||
Pattern::UnwrappedOpaque {
|
||||
whole_var: var_store.fresh(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::expr::Expr;
|
||||
use crate::pattern::Pattern;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
|
||||
|
@ -46,6 +46,23 @@ impl ReferencesBitflags {
|
|||
const TYPE_LOOKUP: Self = ReferencesBitflags(2);
|
||||
const CALL: Self = ReferencesBitflags(4);
|
||||
const BOUND: Self = ReferencesBitflags(8);
|
||||
const QUALIFIED: Self = ReferencesBitflags(16);
|
||||
const UNQUALIFIED: Self = ReferencesBitflags(32);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum QualifiedReference {
|
||||
Unqualified,
|
||||
Qualified,
|
||||
}
|
||||
|
||||
impl QualifiedReference {
|
||||
fn flags(&self, flags: ReferencesBitflags) -> ReferencesBitflags {
|
||||
match self {
|
||||
Self::Unqualified => ReferencesBitflags(flags.0 | ReferencesBitflags::UNQUALIFIED.0),
|
||||
Self::Qualified => ReferencesBitflags(flags.0 | ReferencesBitflags::QUALIFIED.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -108,12 +125,12 @@ impl References {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn insert_value_lookup(&mut self, symbol: Symbol) {
|
||||
self.insert(symbol, ReferencesBitflags::VALUE_LOOKUP);
|
||||
pub fn insert_value_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) {
|
||||
self.insert(symbol, qualified.flags(ReferencesBitflags::VALUE_LOOKUP));
|
||||
}
|
||||
|
||||
pub fn insert_type_lookup(&mut self, symbol: Symbol) {
|
||||
self.insert(symbol, ReferencesBitflags::TYPE_LOOKUP);
|
||||
pub fn insert_type_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) {
|
||||
self.insert(symbol, qualified.flags(ReferencesBitflags::TYPE_LOOKUP));
|
||||
}
|
||||
|
||||
pub fn insert_bound(&mut self, symbol: Symbol) {
|
||||
|
@ -178,7 +195,24 @@ impl References {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn has_unqualified_type_or_value_lookup(&self, symbol: Symbol) -> bool {
|
||||
let mask = ReferencesBitflags::VALUE_LOOKUP.0 | ReferencesBitflags::TYPE_LOOKUP.0;
|
||||
let it = self.symbols.iter().zip(self.bitflags.iter());
|
||||
|
||||
for (a, b) in it {
|
||||
if *a == symbol && b.0 & mask > 0 && b.0 & ReferencesBitflags::UNQUALIFIED.0 > 0 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn references_type_def(&self, symbol: Symbol) -> bool {
|
||||
self.has_type_lookup(symbol)
|
||||
}
|
||||
|
||||
pub fn has_module_lookup(&self, module_id: ModuleId) -> bool {
|
||||
self.symbols.iter().any(|sym| sym.module_id() == module_id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
|
||||
use roc_module::ident::{Ident, ModuleName};
|
||||
use roc_module::symbol::{IdentId, IdentIds, ModuleId, ScopeModules, Symbol};
|
||||
use roc_problem::can::RuntimeError;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
|
@ -29,8 +29,11 @@ pub struct Scope {
|
|||
/// The first `exposed_ident_count` identifiers are exposed
|
||||
exposed_ident_count: usize,
|
||||
|
||||
/// Identifiers that are imported (and introduced in the header)
|
||||
imports: Vec<(Ident, Symbol, Region)>,
|
||||
/// Modules that are imported
|
||||
pub modules: ScopeModules,
|
||||
|
||||
/// Identifiers that are imported
|
||||
imported_symbols: Vec<(Ident, Symbol, Region)>,
|
||||
|
||||
/// Shadows of an ability member, for example a local specialization of `eq` for the ability
|
||||
/// member `Eq implements eq : a, a -> Bool where a implements Eq` gets a shadow symbol it can use for its
|
||||
|
@ -50,6 +53,7 @@ pub struct Scope {
|
|||
impl Scope {
|
||||
pub fn new(
|
||||
home: ModuleId,
|
||||
module_name: ModuleName,
|
||||
initial_ident_ids: IdentIds,
|
||||
starting_abilities_store: PendingAbilitiesStore,
|
||||
) -> Scope {
|
||||
|
@ -66,7 +70,8 @@ impl Scope {
|
|||
aliases: VecMap::default(),
|
||||
abilities_store: starting_abilities_store,
|
||||
shadows: VecMap::default(),
|
||||
imports: default_imports,
|
||||
modules: ScopeModules::new(home, module_name),
|
||||
imported_symbols: default_imports,
|
||||
ignored_locals: VecMap::default(),
|
||||
}
|
||||
}
|
||||
|
@ -80,9 +85,9 @@ impl Scope {
|
|||
}
|
||||
|
||||
pub fn add_docs_imports(&mut self) {
|
||||
self.imports
|
||||
self.imported_symbols
|
||||
.push(("Dict".into(), Symbol::DICT_DICT, Region::zero()));
|
||||
self.imports
|
||||
self.imported_symbols
|
||||
.push(("Set".into(), Symbol::SET_SET, Region::zero()));
|
||||
}
|
||||
|
||||
|
@ -111,7 +116,7 @@ impl Scope {
|
|||
|
||||
fn idents_in_scope(&self) -> impl Iterator<Item = Ident> + '_ {
|
||||
let it1 = self.locals.idents_in_scope();
|
||||
let it2 = self.imports.iter().map(|t| t.0.clone());
|
||||
let it2 = self.imported_symbols.iter().map(|t| t.0.clone());
|
||||
|
||||
it2.chain(it1)
|
||||
}
|
||||
|
@ -137,7 +142,7 @@ impl Scope {
|
|||
},
|
||||
None => {
|
||||
// opaque types can only be wrapped/unwrapped in the scope they are defined in (and below)
|
||||
let error = if let Some((_, decl_region)) = self.has_imported(opaque_str) {
|
||||
let error = if let Some((_, decl_region)) = self.has_imported_symbol(opaque_str) {
|
||||
// specific error for when the opaque is imported, which definitely does not work
|
||||
RuntimeError::OpaqueOutsideScope {
|
||||
opaque,
|
||||
|
@ -200,8 +205,8 @@ impl Scope {
|
|||
}
|
||||
}
|
||||
|
||||
fn has_imported(&self, ident: &str) -> Option<(Symbol, Region)> {
|
||||
for (import, shadow, original_region) in self.imports.iter() {
|
||||
fn has_imported_symbol(&self, ident: &str) -> Option<(Symbol, Region)> {
|
||||
for (import, shadow, original_region) in self.imported_symbols.iter() {
|
||||
if ident == import.as_str() {
|
||||
return Some((*shadow, *original_region));
|
||||
}
|
||||
|
@ -213,7 +218,7 @@ impl Scope {
|
|||
/// Is an identifier in scope, either in the locals or imports
|
||||
fn scope_contains_ident(&self, ident: &str) -> ContainsIdent {
|
||||
// exposed imports are likely to be small
|
||||
match self.has_imported(ident) {
|
||||
match self.has_imported_symbol(ident) {
|
||||
Some((symbol, region)) => ContainsIdent::InScope(symbol, region),
|
||||
None => self.locals.contains_ident(ident),
|
||||
}
|
||||
|
@ -377,19 +382,19 @@ impl Scope {
|
|||
///
|
||||
/// Returns Err if this would shadow an existing ident, including the
|
||||
/// Symbol and Region of the ident we already had in scope under that name.
|
||||
pub fn import(
|
||||
pub fn import_symbol(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
symbol: Symbol,
|
||||
region: Region,
|
||||
) -> Result<(), (Symbol, Region)> {
|
||||
if let Some((s, r)) = self.has_imported(ident.as_str()) {
|
||||
return Err((s, r));
|
||||
match self.scope_contains_ident(ident.as_str()) {
|
||||
ContainsIdent::InScope(symbol, region) => Err((symbol, region)),
|
||||
ContainsIdent::NotPresent | ContainsIdent::NotInScope(_) => {
|
||||
self.imported_symbols.push((ident, symbol, region));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
self.imports.push((ident, symbol, region));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_alias(
|
||||
|
@ -421,17 +426,22 @@ impl Scope {
|
|||
//
|
||||
// - abilities_store: ability definitions not allowed in inner scopes
|
||||
// - locals: everything introduced in the inner scope is marked as not in scope in the rollback
|
||||
// - imports: everything that was imported in the inner scope is removed in the rollback
|
||||
// - aliases: stored in a VecMap, we just discard anything added in an inner scope
|
||||
// - exposed_ident_count: unchanged
|
||||
// - home: unchanged
|
||||
let aliases_count = self.aliases.len();
|
||||
let ignored_locals_count = self.ignored_locals.len();
|
||||
let locals_snapshot = self.locals.in_scope.len();
|
||||
let imported_symbols_snapshot = self.imported_symbols.len();
|
||||
let imported_modules_snapshot = self.modules.len();
|
||||
|
||||
let result = f(self);
|
||||
|
||||
self.aliases.truncate(aliases_count);
|
||||
self.ignored_locals.truncate(ignored_locals_count);
|
||||
self.imported_symbols.truncate(imported_symbols_snapshot);
|
||||
self.modules.truncate(imported_modules_snapshot);
|
||||
|
||||
// anything added in the inner scope is no longer in scope now
|
||||
for i in locals_snapshot..self.locals.in_scope.len() {
|
||||
|
@ -649,6 +659,7 @@ mod test {
|
|||
let _register_module_debug_names = ModuleIds::default();
|
||||
let mut scope = Scope::new(
|
||||
ModuleId::ATTR,
|
||||
"#Attr".into(),
|
||||
IdentIds::default(),
|
||||
PendingAbilitiesStore::default(),
|
||||
);
|
||||
|
@ -668,6 +679,7 @@ mod test {
|
|||
let _register_module_debug_names = ModuleIds::default();
|
||||
let mut scope = Scope::new(
|
||||
ModuleId::ATTR,
|
||||
"#Attr".into(),
|
||||
IdentIds::default(),
|
||||
PendingAbilitiesStore::default(),
|
||||
);
|
||||
|
@ -697,6 +709,7 @@ mod test {
|
|||
let _register_module_debug_names = ModuleIds::default();
|
||||
let mut scope = Scope::new(
|
||||
ModuleId::ATTR,
|
||||
"#Attr".into(),
|
||||
IdentIds::default(),
|
||||
PendingAbilitiesStore::default(),
|
||||
);
|
||||
|
@ -718,6 +731,7 @@ mod test {
|
|||
let _register_module_debug_names = ModuleIds::default();
|
||||
let scope = Scope::new(
|
||||
ModuleId::ATTR,
|
||||
"#Attr".into(),
|
||||
IdentIds::default(),
|
||||
PendingAbilitiesStore::default(),
|
||||
);
|
||||
|
@ -735,6 +749,7 @@ mod test {
|
|||
let _register_module_debug_names = ModuleIds::default();
|
||||
let mut scope = Scope::new(
|
||||
ModuleId::ATTR,
|
||||
"#Attr".into(),
|
||||
IdentIds::default(),
|
||||
PendingAbilitiesStore::default(),
|
||||
);
|
||||
|
@ -796,6 +811,7 @@ mod test {
|
|||
let _register_module_debug_names = ModuleIds::default();
|
||||
let mut scope = Scope::new(
|
||||
ModuleId::ATTR,
|
||||
"#Attr".into(),
|
||||
IdentIds::default(),
|
||||
PendingAbilitiesStore::default(),
|
||||
);
|
||||
|
@ -806,7 +822,7 @@ mod test {
|
|||
|
||||
assert!(scope.lookup(&ident, region).is_err());
|
||||
|
||||
assert!(scope.import(ident.clone(), symbol, region).is_ok());
|
||||
assert!(scope.import_symbol(ident.clone(), symbol, region).is_ok());
|
||||
|
||||
assert!(scope.lookup(&ident, region).is_ok());
|
||||
|
||||
|
@ -818,6 +834,7 @@ mod test {
|
|||
let _register_module_debug_names = ModuleIds::default();
|
||||
let mut scope = Scope::new(
|
||||
ModuleId::ATTR,
|
||||
"#Attr".into(),
|
||||
IdentIds::default(),
|
||||
PendingAbilitiesStore::default(),
|
||||
);
|
||||
|
@ -828,7 +845,7 @@ mod test {
|
|||
let region1 = Region::from_pos(Position { offset: 10 });
|
||||
let region2 = Region::from_pos(Position { offset: 20 });
|
||||
|
||||
scope.import(ident.clone(), symbol, region1).unwrap();
|
||||
scope.import_symbol(ident.clone(), symbol, region1).unwrap();
|
||||
|
||||
let (original, _ident, shadow_symbol) =
|
||||
scope.introduce(ident.clone(), region2).unwrap_err();
|
||||
|
|
|
@ -225,7 +225,7 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
|
|||
|
||||
// Any suffixed arguments will be innermost, therefore we unwrap those first
|
||||
let local_args = arena.alloc_slice_copy(apply_args);
|
||||
for (_, arg) in local_args.iter_mut().enumerate() {
|
||||
for arg in local_args.iter_mut() {
|
||||
match unwrap_suffixed_expression(arena, arg, maybe_def_pat) {
|
||||
Ok(new_arg) => {
|
||||
*arg = new_arg;
|
||||
|
@ -562,7 +562,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
};
|
||||
|
||||
let maybe_suffixed_value_def = match current_value_def {
|
||||
Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) => None,
|
||||
Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None,
|
||||
AnnotatedBody { body_pattern, body_expr, .. } => Some((body_pattern, body_expr)),
|
||||
Body (def_pattern, def_expr, .. ) => Some((def_pattern, def_expr)),
|
||||
};
|
||||
|
|
|
@ -7,12 +7,13 @@ use roc_can::expr::Output;
|
|||
use roc_can::expr::{canonicalize_expr, Expr};
|
||||
use roc_can::scope::Scope;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, PackageModuleIds, Symbol};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{AliasVar, Type};
|
||||
use std::hash::Hash;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn test_home() -> ModuleId {
|
||||
ModuleIds::default().get_or_insert(&"Test".into())
|
||||
|
@ -43,7 +44,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
|||
|
||||
let mut var_store = VarStore::default();
|
||||
let var = var_store.fresh();
|
||||
let module_ids = ModuleIds::default();
|
||||
let qualified_module_ids = PackageModuleIds::default();
|
||||
|
||||
// Desugar operators (convert them to Apply calls, taking into account
|
||||
// operator precedence and associativity rules), before doing other canonicalization.
|
||||
|
@ -60,7 +61,12 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
|||
arena.alloc("TestPath"),
|
||||
);
|
||||
|
||||
let mut scope = Scope::new(home, IdentIds::default(), Default::default());
|
||||
let mut scope = Scope::new(
|
||||
home,
|
||||
"TestPath".into(),
|
||||
IdentIds::default(),
|
||||
Default::default(),
|
||||
);
|
||||
scope.add_alias(
|
||||
Symbol::NUM_INT,
|
||||
Region::zero(),
|
||||
|
@ -74,7 +80,14 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
|||
);
|
||||
|
||||
let dep_idents = IdentIds::exposed_builtins(0);
|
||||
let mut env = Env::new(arena, home, &dep_idents, &module_ids);
|
||||
let mut env = Env::new(
|
||||
arena,
|
||||
home,
|
||||
Path::new("Test.roc"),
|
||||
&dep_idents,
|
||||
&qualified_module_ids,
|
||||
None,
|
||||
);
|
||||
let (loc_expr, output) = canonicalize_expr(
|
||||
&mut env,
|
||||
&mut var_store,
|
||||
|
@ -87,7 +100,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
|
|||
all_ident_ids.insert(home, scope.locals.ident_ids);
|
||||
|
||||
let interns = Interns {
|
||||
module_ids: env.module_ids.clone(),
|
||||
module_ids: env.qualified_module_ids.clone().into_module_ids(),
|
||||
all_ident_ids,
|
||||
};
|
||||
|
||||
|
|
|
@ -3682,7 +3682,7 @@ fn constraint_recursive_function(
|
|||
signature_closure_type,
|
||||
ret_type,
|
||||
),
|
||||
_ => todo!("TODO {:?}", (loc_symbol, &signature)),
|
||||
_ => todo!("TODO {:?}", (loc_symbol, types[signature])),
|
||||
};
|
||||
|
||||
let region = loc_function_def.region;
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use crate::annotation::{Formattable, Newlines, Parens};
|
||||
use crate::annotation::{is_collection_multiline, Formattable, Newlines, Parens};
|
||||
use crate::collection::{fmt_collection, Braces};
|
||||
use crate::expr::fmt_str_literal;
|
||||
use crate::pattern::fmt_pattern;
|
||||
use crate::spaces::{fmt_default_newline, fmt_spaces, INDENT};
|
||||
use crate::spaces::{fmt_default_newline, fmt_default_spaces, fmt_spaces, INDENT};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{
|
||||
AbilityMember, Defs, Expr, ExtractSpaces, Pattern, Spaces, StrLiteral, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef,
|
||||
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
||||
ImportedModuleName, IngestedFileImport, ModuleImport, Pattern, Spaces, StrLiteral,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use roc_parse::header::Keyword;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
/// A Located formattable value is also formattable
|
||||
|
@ -183,6 +187,180 @@ impl<'a> Formattable for TypeHeader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for ModuleImport<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
let Self {
|
||||
before_name,
|
||||
name,
|
||||
alias,
|
||||
exposed,
|
||||
} = self;
|
||||
|
||||
!before_name.is_empty()
|
||||
|| name.is_multiline()
|
||||
|| alias.is_multiline()
|
||||
|| match exposed {
|
||||
Some(a) => a.keyword.is_multiline() || is_collection_multiline(&a.item),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
let Self {
|
||||
before_name,
|
||||
name,
|
||||
alias,
|
||||
exposed,
|
||||
} = self;
|
||||
|
||||
buf.indent(indent);
|
||||
buf.push_str("import");
|
||||
|
||||
let indent = indent + INDENT;
|
||||
|
||||
fmt_default_spaces(buf, before_name, indent);
|
||||
|
||||
buf.indent(indent);
|
||||
name.format(buf, indent);
|
||||
|
||||
if let Some(alias) = alias {
|
||||
alias.format(buf, indent);
|
||||
}
|
||||
|
||||
if let Some(exposed) = exposed {
|
||||
exposed.keyword.format(buf, indent);
|
||||
|
||||
let list_indent = if !before_name.is_empty()
|
||||
|| alias.is_multiline()
|
||||
|| exposed.keyword.is_multiline()
|
||||
{
|
||||
indent
|
||||
} else {
|
||||
// Align list with import keyword
|
||||
indent - INDENT
|
||||
};
|
||||
|
||||
fmt_collection(buf, list_indent, Braces::Square, exposed.item, Newlines::No);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for IngestedFileImport<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
let Self {
|
||||
before_path,
|
||||
path: _,
|
||||
name,
|
||||
} = self;
|
||||
!before_path.is_empty() || name.is_multiline()
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
let Self {
|
||||
before_path,
|
||||
path,
|
||||
name,
|
||||
} = self;
|
||||
|
||||
buf.indent(indent);
|
||||
buf.push_str("import");
|
||||
|
||||
let indent = indent + INDENT;
|
||||
|
||||
fmt_default_spaces(buf, before_path, indent);
|
||||
fmt_str_literal(buf, path.value, indent);
|
||||
name.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for ImportedModuleName<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
// No newlines in module name itself.
|
||||
false
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.indent(indent);
|
||||
|
||||
if let Some(package_shorthand) = self.package {
|
||||
buf.push_str(package_shorthand);
|
||||
buf.push_str(".");
|
||||
}
|
||||
|
||||
self.name.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for ImportAlias<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
// No newlines in alias itself.
|
||||
false
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.indent(indent);
|
||||
buf.push_str(self.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
impl Formattable for ImportAsKeyword {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf<'_>,
|
||||
_parens: crate::annotation::Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.indent(indent);
|
||||
buf.push_str(ImportAsKeyword::KEYWORD);
|
||||
}
|
||||
}
|
||||
|
||||
impl Formattable for ImportExposingKeyword {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf<'_>,
|
||||
_parens: crate::annotation::Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.indent(indent);
|
||||
buf.push_str(ImportExposingKeyword::KEYWORD);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for ValueDef<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use roc_parse::ast::ValueDef::*;
|
||||
|
@ -196,6 +374,8 @@ impl<'a> Formattable for ValueDef<'a> {
|
|||
Expect { condition, .. } => condition.is_multiline(),
|
||||
ExpectFx { condition, .. } => condition.is_multiline(),
|
||||
Dbg { condition, .. } => condition.is_multiline(),
|
||||
ModuleImport(module_import) => module_import.is_multiline(),
|
||||
IngestedFileImport(ingested_file_import) => ingested_file_import.is_multiline(),
|
||||
Stmt(loc_expr) => loc_expr.is_multiline(),
|
||||
}
|
||||
}
|
||||
|
@ -239,6 +419,8 @@ impl<'a> Formattable for ValueDef<'a> {
|
|||
buf.newline();
|
||||
fmt_body(buf, &body_pattern.value, &body_expr.value, indent);
|
||||
}
|
||||
ModuleImport(module_import) => module_import.format(buf, indent),
|
||||
IngestedFileImport(ingested_file_import) => ingested_file_import.format(buf, indent),
|
||||
Stmt(loc_expr) => loc_expr.format_with_options(buf, parens, newlines, indent),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ impl<'a> Formattable for Expr<'a> {
|
|||
| MalformedClosure
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| IngestedFile(_, _)
|
||||
| EmptyDefsFinal
|
||||
| Crash => false,
|
||||
|
||||
|
@ -530,7 +529,6 @@ impl<'a> Formattable for Expr<'a> {
|
|||
PrecedenceConflict { .. } => {}
|
||||
MultipleRecordBuilders { .. } => {}
|
||||
UnappliedRecordBuilder { .. } => {}
|
||||
IngestedFile(_, _) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ use crate::spaces::RemoveSpaces;
|
|||
use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT};
|
||||
use crate::Buf;
|
||||
use bumpalo::Bump;
|
||||
use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
|
||||
use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces};
|
||||
use roc_parse::header::{
|
||||
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
||||
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry, PackageHeader,
|
||||
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires,
|
||||
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader,
|
||||
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword,
|
||||
PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
|
||||
WithKeyword,
|
||||
};
|
||||
use roc_parse::ident::UppercaseIdent;
|
||||
use roc_region::all::Loc;
|
||||
|
@ -18,8 +19,8 @@ use roc_region::all::Loc;
|
|||
pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) {
|
||||
fmt_comments_only(buf, module.comments.iter(), NewlineAt::Bottom, 0);
|
||||
match &module.header {
|
||||
Header::Interface(header) => {
|
||||
fmt_interface_header(buf, header);
|
||||
Header::Module(header) => {
|
||||
fmt_module_header(buf, header);
|
||||
}
|
||||
Header::App(header) => {
|
||||
fmt_app_header(buf, header);
|
||||
|
@ -75,6 +76,7 @@ keywords! {
|
|||
RequiresKeyword,
|
||||
ProvidesKeyword,
|
||||
ToKeyword,
|
||||
PlatformKeyword,
|
||||
}
|
||||
|
||||
impl<V: Formattable> Formattable for Option<V> {
|
||||
|
@ -171,20 +173,12 @@ impl<'a, K: Formattable, V: Formattable> Formattable for KeywordItem<'a, K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fmt_interface_header<'a>(buf: &mut Buf, header: &'a InterfaceHeader<'a>) {
|
||||
pub fn fmt_module_header<'a>(buf: &mut Buf, header: &'a ModuleHeader<'a>) {
|
||||
buf.indent(0);
|
||||
buf.push_str("interface");
|
||||
let indent = INDENT;
|
||||
fmt_default_spaces(buf, header.before_name, indent);
|
||||
buf.push_str("module");
|
||||
|
||||
// module name
|
||||
buf.indent(indent);
|
||||
buf.push_str(header.name.value.as_str());
|
||||
|
||||
header.exposes.keyword.format(buf, indent);
|
||||
fmt_exposes(buf, header.exposes.item, indent);
|
||||
header.imports.keyword.format(buf, indent);
|
||||
fmt_imports(buf, header.imports.item, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_exposes, INDENT);
|
||||
fmt_exposes(buf, header.exposes, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {
|
||||
|
@ -207,34 +201,33 @@ pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {
|
|||
pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) {
|
||||
buf.indent(0);
|
||||
buf.push_str("app");
|
||||
let indent = INDENT;
|
||||
fmt_default_spaces(buf, header.before_name, indent);
|
||||
|
||||
fmt_str_literal(buf, header.name.value, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_provides, INDENT);
|
||||
fmt_exposes(buf, header.provides, indent);
|
||||
|
||||
if let Some(packages) = &header.packages {
|
||||
packages.keyword.format(buf, indent);
|
||||
fmt_packages(buf, packages.item, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_packages, INDENT);
|
||||
fmt_packages(buf, header.packages.value, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_spaces_with_outdent(buf: &mut Buf, spaces: &[CommentOrNewline], indent: u16) -> u16 {
|
||||
if spaces.iter().all(|c| c.is_newline()) {
|
||||
buf.spaces(1);
|
||||
0
|
||||
} else {
|
||||
fmt_default_spaces(buf, spaces, indent);
|
||||
indent
|
||||
}
|
||||
if let Some(imports) = &header.imports {
|
||||
imports.keyword.format(buf, indent);
|
||||
fmt_imports(buf, imports.item, indent);
|
||||
}
|
||||
header.provides.format(buf, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_package_header<'a>(buf: &mut Buf, header: &'a PackageHeader<'a>) {
|
||||
buf.indent(0);
|
||||
buf.push_str("package");
|
||||
let indent = INDENT;
|
||||
fmt_default_spaces(buf, header.before_name, indent);
|
||||
|
||||
fmt_package_name(buf, header.name.value, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_exposes, INDENT);
|
||||
fmt_exposes(buf, header.exposes, indent);
|
||||
|
||||
header.exposes.keyword.format(buf, indent);
|
||||
fmt_exposes(buf, header.exposes.item, indent);
|
||||
header.packages.keyword.format(buf, indent);
|
||||
fmt_packages(buf, header.packages.item, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_packages, INDENT);
|
||||
fmt_packages(buf, header.packages.value, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_platform_header<'a>(buf: &mut Buf, header: &'a PlatformHeader<'a>) {
|
||||
|
@ -465,6 +458,15 @@ fn fmt_packages_entry(buf: &mut Buf, entry: &PackageEntry, indent: u16) {
|
|||
buf.push_str(entry.shorthand);
|
||||
buf.push(':');
|
||||
fmt_default_spaces(buf, entry.spaces_after_shorthand, indent);
|
||||
|
||||
let indent = indent + INDENT;
|
||||
|
||||
if let Some(spaces_after) = entry.platform_marker {
|
||||
buf.indent(indent);
|
||||
buf.push_str(roc_parse::keyword::PLATFORM);
|
||||
fmt_default_spaces(buf, spaces_after, indent);
|
||||
}
|
||||
|
||||
fmt_package_name(buf, entry.package_name.value, indent);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,15 @@ use roc_module::called_via::{BinOp, UnaryOp};
|
|||
use roc_parse::{
|
||||
ast::{
|
||||
AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr,
|
||||
Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, Module,
|
||||
Pattern, PatternAs, RecordBuilderField, Spaced, Spaces, StrLiteral, StrSegment, Tag,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch,
|
||||
Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias,
|
||||
ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileImport, Module,
|
||||
ModuleImport, Pattern, PatternAs, RecordBuilderField, Spaced, Spaces, StrLiteral,
|
||||
StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch,
|
||||
},
|
||||
header::{
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
|
||||
ModuleName, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires,
|
||||
ProvidesTo, To, TypedIdent,
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, KeywordItem, ModuleHeader, ModuleName,
|
||||
PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To,
|
||||
TypedIdent,
|
||||
},
|
||||
ident::{BadIdent, UppercaseIdent},
|
||||
};
|
||||
|
@ -282,23 +283,25 @@ impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> {
|
|||
impl<'a> RemoveSpaces<'a> for Module<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let header = match &self.header {
|
||||
Header::Interface(header) => Header::Interface(InterfaceHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
Header::Module(header) => Header::Module(ModuleHeader {
|
||||
before_exposes: &[],
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
interface_imports: header.interface_imports.remove_spaces(arena),
|
||||
}),
|
||||
Header::App(header) => Header::App(AppHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
before_provides: &[],
|
||||
provides: header.provides.remove_spaces(arena),
|
||||
before_packages: &[],
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
old_imports: header.old_imports.remove_spaces(arena),
|
||||
old_provides_to_new_package: header
|
||||
.old_provides_to_new_package
|
||||
.remove_spaces(arena),
|
||||
}),
|
||||
Header::Package(header) => Header::Package(PackageHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
before_exposes: &[],
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
before_packages: &[],
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
}),
|
||||
Header::Platform(header) => Header::Platform(PlatformHeader {
|
||||
|
@ -405,6 +408,10 @@ impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
|||
PackageEntry {
|
||||
shorthand: self.shorthand,
|
||||
spaces_after_shorthand: &[],
|
||||
platform_marker: match self.platform_marker {
|
||||
Some(_) => Some(&[]),
|
||||
None => None,
|
||||
},
|
||||
package_name: self.package_name.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
|
@ -567,11 +574,63 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
|||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)),
|
||||
IngestedFileImport(ingested_file_import) => {
|
||||
IngestedFileImport(ingested_file_import.remove_spaces(arena))
|
||||
}
|
||||
Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleImport<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ModuleImport {
|
||||
before_name: &[],
|
||||
name: self.name.remove_spaces(arena),
|
||||
alias: self.alias.remove_spaces(arena),
|
||||
exposed: self.exposed.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
IngestedFileImport {
|
||||
before_path: &[],
|
||||
path: self.path.remove_spaces(arena),
|
||||
name: self.name.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ImportedModuleName {
|
||||
package: self.package.remove_spaces(arena),
|
||||
name: self.name.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportAlias<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportAsKeyword {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportExposingKeyword {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Implements<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
Implements::Implements
|
||||
|
@ -682,7 +741,6 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
|||
is_negative,
|
||||
},
|
||||
Expr::Str(a) => Expr::Str(a.remove_spaces(arena)),
|
||||
Expr::IngestedFile(a, b) => Expr::IngestedFile(a, b),
|
||||
Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b),
|
||||
Expr::AccessorFunction(a) => Expr::AccessorFunction(a),
|
||||
Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b),
|
||||
|
|
|
@ -1124,7 +1124,11 @@ pub fn construct_optimization_passes<'a>(
|
|||
}
|
||||
OptLevel::Size => {
|
||||
pmb.set_optimization_level(OptimizationLevel::Default);
|
||||
// 2 is equivalent to `-Oz`.
|
||||
pmb.set_size_level(2);
|
||||
|
||||
// TODO: For some usecase, like embedded, it is useful to expose this and tune it.
|
||||
// This really depends on if inlining causes enough simplifications to reduce code size.
|
||||
pmb.set_inliner_with_threshold(50);
|
||||
}
|
||||
OptLevel::Optimize => {
|
||||
|
@ -1134,9 +1138,10 @@ pub fn construct_optimization_passes<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
// Add optimization passes for Size and Optimize.
|
||||
if matches!(opt_level, OptLevel::Size | OptLevel::Optimize) {
|
||||
// TODO figure out which of these actually help
|
||||
// Add extra optimization passes for Optimize.
|
||||
if matches!(opt_level, OptLevel::Optimize) {
|
||||
// TODO: figure out which of these actually help.
|
||||
// Note, llvm probably already runs all of these as part of Aggressive.
|
||||
|
||||
// function passes
|
||||
|
||||
|
|
|
@ -2161,13 +2161,13 @@ impl<'a> LowLevelCall<'a> {
|
|||
|
||||
// Empty record is always equal to empty record.
|
||||
// There are no runtime arguments to check, so just emit true or false.
|
||||
LayoutRepr::Struct(field_layouts) if field_layouts.is_empty() => {
|
||||
LayoutRepr::Struct([]) => {
|
||||
backend.code_builder.i32_const(!invert_result as i32);
|
||||
}
|
||||
|
||||
// Void is always equal to void. This is the type for the contents of the empty list in `[] == []`
|
||||
// This instruction will never execute, but we need an i32 for module validation
|
||||
LayoutRepr::Union(UnionLayout::NonRecursive(tags)) if tags.is_empty() => {
|
||||
LayoutRepr::Union(UnionLayout::NonRecursive([])) => {
|
||||
backend.code_builder.i32_const(!invert_result as i32);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use roc_can::scope::Scope;
|
|||
use roc_collections::all::{ImMap, MutMap, SendSet};
|
||||
use roc_constrain::expr::constrain_expr;
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds};
|
||||
use roc_parse::parser::{SourceError, SyntaxError};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::Loc;
|
||||
|
@ -154,10 +154,10 @@ pub fn can_expr_with<'a>(
|
|||
let var = var_store.fresh();
|
||||
let var_index = constraints.push_variable(var);
|
||||
let expected = constraints.push_expected_type(Expected::NoExpectation(var_index));
|
||||
let mut module_ids = ModuleIds::default();
|
||||
let mut module_ids = PackageModuleIds::default();
|
||||
|
||||
// ensure the Test module is accessible in our tests
|
||||
module_ids.get_or_insert(&"Test".into());
|
||||
module_ids.get_or_insert(&PQModuleName::Unqualified("Test".into()));
|
||||
|
||||
// Desugar operators (convert them to Apply calls, taking into account
|
||||
// operator precedence and associativity rules), before doing other canonicalization.
|
||||
|
@ -174,10 +174,22 @@ pub fn can_expr_with<'a>(
|
|||
arena.alloc("TestPath"),
|
||||
);
|
||||
|
||||
let mut scope = Scope::new(home, IdentIds::default(), Default::default());
|
||||
let mut scope = Scope::new(
|
||||
home,
|
||||
"TestPath".into(),
|
||||
IdentIds::default(),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let dep_idents = IdentIds::exposed_builtins(0);
|
||||
let mut env = Env::new(arena, home, &dep_idents, &module_ids);
|
||||
let mut env = Env::new(
|
||||
arena,
|
||||
home,
|
||||
Path::new("Test.roc"),
|
||||
&dep_idents,
|
||||
&module_ids,
|
||||
None,
|
||||
);
|
||||
let (loc_expr, output) = canonicalize_expr(
|
||||
&mut env,
|
||||
&mut var_store,
|
||||
|
@ -203,7 +215,7 @@ pub fn can_expr_with<'a>(
|
|||
all_ident_ids.insert(home, scope.locals.ident_ids);
|
||||
|
||||
let interns = Interns {
|
||||
module_ids: env.module_ids.clone(),
|
||||
module_ids: env.qualified_module_ids.clone().into_module_ids(),
|
||||
all_ident_ids,
|
||||
};
|
||||
|
||||
|
|
8
crates/compiler/load/tests/platform.roc
Normal file
8
crates/compiler/load/tests/platform.roc
Normal file
|
@ -0,0 +1,8 @@
|
|||
platform "test-platform"
|
||||
requires {} { main : * }
|
||||
exposes []
|
||||
packages {}
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : {} -> {}
|
||||
mainForHost = \{} -> {}
|
|
@ -4561,7 +4561,7 @@ mod test_reporting {
|
|||
test_report!(
|
||||
comment_with_control_character,
|
||||
"# comment with a \x07\n",
|
||||
@r"
|
||||
@r###"
|
||||
── ASCII CONTROL CHARACTER in tmp/comment_with_control_character/Test.roc ──────
|
||||
|
||||
I encountered an ASCII control character:
|
||||
|
@ -4570,7 +4570,7 @@ mod test_reporting {
|
|||
^
|
||||
|
||||
ASCII control characters are not allowed.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -4919,20 +4919,20 @@ mod test_reporting {
|
|||
dict_type_formatting,
|
||||
indoc!(
|
||||
r#"
|
||||
app "dict" imports [ Dict ] provides [main] to "./platform"
|
||||
app "dict" imports [] provides [main] to "./platform"
|
||||
|
||||
myDict : Dict.Dict Num.I64 Str
|
||||
myDict : Dict Num.I64 Str
|
||||
myDict = Dict.insert (Dict.empty {}) "foo" 42
|
||||
|
||||
main = myDict
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
Something is off with the body of the `myDict` definition:
|
||||
|
||||
3│ myDict : Dict.Dict Num.I64 Str
|
||||
3│ myDict : Dict Num.I64 Str
|
||||
4│ myDict = Dict.insert (Dict.empty {}) "foo" 42
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -4943,14 +4943,14 @@ mod test_reporting {
|
|||
But the type annotation on `myDict` says it should be:
|
||||
|
||||
Dict I64 Str
|
||||
"#
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
alias_type_diff,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Set.{ Set }] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
HSet a : Set a
|
||||
|
||||
|
@ -6109,9 +6109,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
report_header_problem_as(
|
||||
indoc!(
|
||||
r"
|
||||
interface Foobar
|
||||
exposes [main, @Foo]
|
||||
imports [pf.Task, Base64]
|
||||
module [main, @Foo]
|
||||
"
|
||||
),
|
||||
indoc!(
|
||||
|
@ -6120,39 +6118,12 @@ In roc, functions are always written as a lambda, like{}
|
|||
|
||||
I am partway through parsing an `exposes` list, but I got stuck here:
|
||||
|
||||
1│ interface Foobar
|
||||
2│ exposes [main, @Foo]
|
||||
^
|
||||
1│ module [main, @Foo]
|
||||
^
|
||||
|
||||
I was expecting a type name, value name or function name next, like
|
||||
|
||||
exposes [Animal, default, tame]
|
||||
"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_module_name() {
|
||||
report_header_problem_as(
|
||||
indoc!(
|
||||
r"
|
||||
interface foobar
|
||||
exposes [main, @Foo]
|
||||
imports [pf.Task, Base64]
|
||||
"
|
||||
),
|
||||
indoc!(
|
||||
r"
|
||||
── WEIRD MODULE NAME in /code/proj/Main.roc ────────────────────────────────────
|
||||
|
||||
I am partway through parsing a header, but got stuck here:
|
||||
|
||||
1│ interface foobar
|
||||
^
|
||||
|
||||
I am expecting a module name next, like BigNum or Main. Module names
|
||||
must start with an uppercase letter.
|
||||
[Animal, default, tame]
|
||||
"
|
||||
),
|
||||
)
|
||||
|
@ -9310,7 +9281,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
type_error_in_apply_is_circular,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Set] provides [go] to "./platform"
|
||||
app "test" imports [] provides [go] to "./platform"
|
||||
|
||||
S a : { set : Set.Set a }
|
||||
|
||||
|
@ -10898,7 +10869,9 @@ In roc, functions are always written as a lambda, like{}
|
|||
function_cannot_derive_encoding,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
myDecoder : Decoder (a -> a) fmt where fmt implements DecoderFormatting
|
||||
|
@ -10907,12 +10880,12 @@ In roc, functions are always written as a lambda, like{}
|
|||
myDecoder
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
5│ myDecoder = decoder
|
||||
7│ myDecoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
@ -10920,14 +10893,16 @@ In roc, functions are always written as a lambda, like{}
|
|||
a -> a
|
||||
|
||||
Note: `Decoding` cannot be generated for functions.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
nested_opaque_cannot_derive_encoding,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
A := {}
|
||||
|
||||
|
@ -10938,12 +10913,12 @@ In roc, functions are always written as a lambda, like{}
|
|||
myDecoder
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
7│ myDecoder = decoder
|
||||
9│ myDecoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
@ -10958,7 +10933,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
|
||||
Tip: `A` does not implement `Decoding`. Consider adding a custom
|
||||
implementation or `implements Decode.Decoding` to the definition of `A`.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -11119,7 +11094,9 @@ In roc, functions are always written as a lambda, like{}
|
|||
infer_decoded_record_error_with_function_field,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [TotallyNotJson] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
import TotallyNotJson
|
||||
|
||||
main =
|
||||
decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes TotallyNotJson.json
|
||||
|
@ -11128,12 +11105,12 @@ In roc, functions are always written as a lambda, like{}
|
|||
_ -> "something went wrong"
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
6│ Ok rcd -> rcd.first rcd.second
|
||||
8│ Ok rcd -> rcd.first rcd.second
|
||||
^^^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
@ -11141,14 +11118,16 @@ In roc, functions are always written as a lambda, like{}
|
|||
* -> *
|
||||
|
||||
Note: `Decoding` cannot be generated for functions.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
record_with_optional_field_types_cannot_derive_decoding,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
myDecoder : Decoder {x : Str, y ? Str} fmt where fmt implements DecoderFormatting
|
||||
|
@ -11157,12 +11136,12 @@ In roc, functions are always written as a lambda, like{}
|
|||
myDecoder
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
5│ myDecoder = decoder
|
||||
7│ myDecoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
@ -11177,7 +11156,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
over records that may or may not contain them at compile time, but are
|
||||
not a concept that extends to runtime!
|
||||
Maybe you wanted to use a `Result`?
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -11359,21 +11338,23 @@ In roc, functions are always written as a lambda, like{}
|
|||
unused_value_import,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [List.{ concat }] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
import List exposing [concat]
|
||||
|
||||
main = ""
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
@r###"
|
||||
── UNUSED IMPORT in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
`List.concat` is not used in this module.
|
||||
List is imported but not used.
|
||||
|
||||
1│ app "test" imports [List.{ concat }] provides [main] to "./platform"
|
||||
^^^^^^
|
||||
3│ import List exposing [concat]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Since `List.concat` isn't used, you don't need to import it.
|
||||
"#
|
||||
Since List isn't used, you don't need to import it.
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -11395,7 +11376,9 @@ In roc, functions are always written as a lambda, like{}
|
|||
unnecessary_builtin_type_import,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode.{ DecodeError }] provides [main, E] to "./platform"
|
||||
app "test" imports [] provides [main, E] to "./platform"
|
||||
|
||||
import Decode exposing [DecodeError]
|
||||
|
||||
E : DecodeError
|
||||
|
||||
|
@ -13664,7 +13647,9 @@ In roc, functions are always written as a lambda, like{}
|
|||
derive_decoding_for_tuple,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
myDecoder : Decoder (U32, Str) fmt where fmt implements DecoderFormatting
|
||||
|
@ -13679,7 +13664,9 @@ In roc, functions are always written as a lambda, like{}
|
|||
cannot_decode_tuple_with_non_decode_element,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
myDecoder : Decoder (U32, {} -> {}) fmt where fmt implements DecoderFormatting
|
||||
|
@ -13688,12 +13675,12 @@ In roc, functions are always written as a lambda, like{}
|
|||
myDecoder
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
5│ myDecoder = decoder
|
||||
7│ myDecoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
@ -13701,7 +13688,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
U32, {} -> {}
|
||||
|
||||
Note: `Decoding` cannot be generated for functions.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_no_problem!(
|
||||
|
|
|
@ -275,6 +275,12 @@ fn generate_entry_docs(
|
|||
ValueDef::ExpectFx { .. } => {
|
||||
// Don't generate docs for `expect-fx`s
|
||||
}
|
||||
ValueDef::ModuleImport { .. } => {
|
||||
// Don't generate docs for module imports
|
||||
}
|
||||
ValueDef::IngestedFileImport { .. } => {
|
||||
// Don't generate docs for ingested file imports
|
||||
}
|
||||
|
||||
ValueDef::Stmt(loc_expr) => {
|
||||
if let roc_parse::ast::Expr::Var {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,7 @@ use roc_module::symbol::{
|
|||
};
|
||||
use roc_mono::ir::{GlueLayouts, HostExposedLambdaSets, LambdaSetId, Proc, ProcLayout, ProcsBase};
|
||||
use roc_mono::layout::{LayoutCache, STLayoutInterner};
|
||||
use roc_parse::ast::{CommentOrNewline, Defs, TypeAnnotation, ValueDef};
|
||||
use roc_parse::ast::{CommentOrNewline, Defs, TypeAnnotation};
|
||||
use roc_parse::header::{HeaderType, PackageName};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_solve::module::Solved;
|
||||
|
@ -96,26 +96,20 @@ pub(crate) struct ModuleHeader<'a> {
|
|||
pub(crate) module_id: ModuleId,
|
||||
pub(crate) module_path: PathBuf,
|
||||
pub(crate) is_root_module: bool,
|
||||
pub(crate) exposed_ident_ids: IdentIds,
|
||||
pub(crate) deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
pub(crate) packages: MutMap<&'a str, PackageName<'a>>,
|
||||
pub(crate) imported_modules: MutMap<ModuleId, Region>,
|
||||
pub(crate) package_qualified_imported_modules: MutSet<PackageQualified<'a, ModuleId>>,
|
||||
pub(crate) exposes: Vec<Symbol>,
|
||||
pub(crate) exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
pub(crate) parse_state: roc_parse::state::State<'a>,
|
||||
pub(crate) header_type: HeaderType<'a>,
|
||||
pub(crate) header_comments: &'a [CommentOrNewline<'a>],
|
||||
pub(crate) symbols_from_requires: Vec<(Loc<Symbol>, Loc<TypeAnnotation<'a>>)>,
|
||||
pub(crate) header_imports: Option<roc_parse::header::ImportsKeywordItem<'a>>,
|
||||
pub(crate) module_timing: ModuleTiming,
|
||||
pub(crate) defined_values: Vec<ValueDef<'a>>,
|
||||
pub(crate) opt_shorthand: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ConstrainedModule {
|
||||
pub(crate) module: Module,
|
||||
pub(crate) declarations: Declarations,
|
||||
pub(crate) imported_modules: MutMap<ModuleId, Region>,
|
||||
pub(crate) available_modules: MutMap<ModuleId, Region>,
|
||||
pub(crate) constraints: Constraints,
|
||||
pub(crate) constraint: ConstraintSoa,
|
||||
pub(crate) ident_ids: IdentIds,
|
||||
|
@ -203,13 +197,17 @@ pub struct ParsedModule<'a> {
|
|||
pub src: &'a str,
|
||||
pub module_timing: ModuleTiming,
|
||||
pub deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
pub imported_modules: MutMap<ModuleId, Region>,
|
||||
pub exposed_ident_ids: IdentIds,
|
||||
pub exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
pub parsed_defs: Defs<'a>,
|
||||
pub symbols_from_requires: Vec<(Loc<Symbol>, Loc<TypeAnnotation<'a>>)>,
|
||||
pub header_type: HeaderType<'a>,
|
||||
pub header_comments: &'a [CommentOrNewline<'a>],
|
||||
pub available_modules: MutMap<ModuleId, Region>,
|
||||
pub package_qualified_available_modules: MutSet<PackageQualified<'a, ModuleId>>,
|
||||
pub packages: MutMap<&'a str, PackageName<'a>>,
|
||||
pub initial_scope: MutMap<Ident, (Symbol, Region)>,
|
||||
pub exposes: Vec<Symbol>,
|
||||
pub opt_shorthand: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -162,10 +162,6 @@ impl<'a> Dependencies<'a> {
|
|||
output.insert((dep, Phase::LoadHeader));
|
||||
}
|
||||
|
||||
// to parse and generate constraints, the headers of all dependencies must be loaded!
|
||||
// otherwise, we don't know whether an imported symbol is actually exposed
|
||||
self.add_dependency_help(module_id, dep, Phase::Parse, Phase::LoadHeader);
|
||||
|
||||
// to canonicalize a module, all its dependencies must be canonicalized
|
||||
self.add_dependency(module_id, dep, Phase::CanonicalizeAndConstrain);
|
||||
|
||||
|
@ -427,10 +423,10 @@ impl<'a> Dependencies<'a> {
|
|||
PrepareStartPhase::Recurse(new)
|
||||
}
|
||||
None => match phase {
|
||||
Phase::LoadHeader => {
|
||||
// this is fine, mark header loading as pending
|
||||
Phase::LoadHeader | Phase::Parse => {
|
||||
// this is fine, mark as pending
|
||||
self.status
|
||||
.insert(Job::Step(module_id, Phase::LoadHeader), Status::Pending);
|
||||
.insert(Job::Step(module_id, phase), Status::Pending);
|
||||
|
||||
PrepareStartPhase::Continue
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
interface Dep1
|
||||
exposes [three, str, Unit, Identity, one, two]
|
||||
imports [Dep3.Blah.{ foo }]
|
||||
imports []
|
||||
|
||||
import Dep3Blah exposing [foo]
|
||||
|
||||
one = 1
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
interface Dep2
|
||||
exposes [one, two, blah]
|
||||
imports [Dep3.Blah.{ foo, bar }]
|
||||
imports []
|
||||
|
||||
import Dep3Blah exposing [foo, bar]
|
||||
|
||||
one = 1
|
||||
|
||||
blah = foo
|
||||
|
||||
two = 2.0
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
interface Dep3.Other
|
||||
exposes [foo, bar]
|
||||
imports []
|
||||
|
||||
foo = "foo from Dep3.Other"
|
||||
bar = "bar from Dep3.Other"
|
|
@ -1,10 +1,12 @@
|
|||
interface Dep3.Blah
|
||||
interface Dep3Blah
|
||||
exposes [one, two, foo, bar]
|
||||
imports [Dep3.Other]
|
||||
imports []
|
||||
|
||||
import Dep3Other
|
||||
|
||||
one = 1
|
||||
|
||||
two = 2
|
||||
|
||||
foo = "foo from Dep3"
|
||||
bar = Dep3.Other.bar
|
||||
bar = Dep3Other.bar
|
6
crates/compiler/load_internal/tests/fixtures/build/app_with_deps/Dep3Other.roc
vendored
Normal file
6
crates/compiler/load_internal/tests/fixtures/build/app_with_deps/Dep3Other.roc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
interface Dep3Other
|
||||
exposes [foo, bar]
|
||||
imports []
|
||||
|
||||
foo = "foo from Dep3Other"
|
||||
bar = "bar from Dep3Other"
|
|
@ -1,7 +1,8 @@
|
|||
interface ImportAlias
|
||||
exposes [unit]
|
||||
imports [Dep1]
|
||||
imports []
|
||||
|
||||
import Dep1
|
||||
|
||||
unit : Dep1.Unit
|
||||
unit = Unit
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
interface OneDep
|
||||
exposes [str]
|
||||
imports [Dep3.Blah.{ foo }]
|
||||
imports []
|
||||
|
||||
import Dep3Blah exposing [foo]
|
||||
|
||||
str = foo
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
interface Primary
|
||||
exposes [blah2, blah3, str, alwaysThree, identity, z, w, succeed, withDefault, yay]
|
||||
imports [Dep1, Dep2.{ two }, Dep3.Blah.{ bar }, Res]
|
||||
imports []
|
||||
|
||||
import Dep1
|
||||
import Dep2
|
||||
import Dep3Blah exposing [bar]
|
||||
import Res
|
||||
|
||||
blah2 = Dep2.two
|
||||
blah3 = bar
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
interface WithBuiltins
|
||||
exposes [floatTest, divisionFn, divisionTest, intTest, constantNum, fromDep2, divDep1ByDep2]
|
||||
imports [Dep1, Dep2.{ two }]
|
||||
imports []
|
||||
|
||||
import Dep1
|
||||
import Dep2 exposing [two]
|
||||
|
||||
floatTest = Num.maxF64
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
interface IngestedFile
|
||||
exposes [str]
|
||||
imports ["IngestedFile.roc" as foo : Str]
|
||||
|
||||
str = foo
|
|
@ -1,6 +1,8 @@
|
|||
interface Dep1
|
||||
exposes [three, str, Unit, Identity, one, two]
|
||||
imports [Dep3.Blah.{ foo }]
|
||||
imports []
|
||||
|
||||
import Dep3 exposing [foo]
|
||||
|
||||
one = 1
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
interface Dep2
|
||||
exposes [one, two, blah]
|
||||
imports [Dep3.Blah.{ foo, bar }]
|
||||
imports []
|
||||
|
||||
import Dep3 exposing [foo, bar]
|
||||
|
||||
one = 1
|
||||
|
||||
blah = foo
|
||||
|
||||
two = 2.0
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
interface Dep3.Blah
|
||||
interface Dep3
|
||||
exposes [one, two, foo, bar]
|
||||
imports []
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
interface ExposedUsedOutsideScope exposes [good, bad] imports []
|
||||
|
||||
good =
|
||||
import Dep2 exposing [two]
|
||||
two
|
||||
|
||||
bad =
|
||||
two
|
|
@ -1,7 +1,8 @@
|
|||
interface ImportAlias
|
||||
exposes [unit]
|
||||
imports [Dep1]
|
||||
imports []
|
||||
|
||||
import Dep1
|
||||
|
||||
unit : Dep1.Unit
|
||||
unit = Unit
|
||||
|
12
crates/compiler/load_internal/tests/fixtures/build/module_with_deps/ImportInsideDef.roc
vendored
Normal file
12
crates/compiler/load_internal/tests/fixtures/build/module_with_deps/ImportInsideDef.roc
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
interface ImportInsideDef exposes [dep1Str, dep2TwoDobuled] imports []
|
||||
|
||||
dep1Str =
|
||||
import Dep1
|
||||
Dep1.str
|
||||
|
||||
dep2TwoDobuled =
|
||||
2
|
||||
* (
|
||||
import Dep2 exposing [two]
|
||||
two
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
interface ImportUsedOutsideScope exposes [good, bad] imports []
|
||||
|
||||
good =
|
||||
import Dep2
|
||||
Dep2.two
|
||||
|
||||
bad =
|
||||
Dep2.two
|
11
crates/compiler/load_internal/tests/fixtures/build/module_with_deps/IngestedFile.roc
vendored
Normal file
11
crates/compiler/load_internal/tests/fixtures/build/module_with_deps/IngestedFile.roc
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
interface IngestedFile
|
||||
exposes [str, nested]
|
||||
imports []
|
||||
|
||||
import "IngestedFile.roc" as foo : Str
|
||||
|
||||
str = foo
|
||||
|
||||
nested =
|
||||
import "Dep1.roc" as dep1 : Str
|
||||
dep1
|
|
@ -1,5 +1,7 @@
|
|||
interface IngestedFileBytes
|
||||
exposes [str]
|
||||
imports ["IngestedFileBytes.roc" as foo : List U8]
|
||||
imports []
|
||||
|
||||
import "IngestedFileBytes.roc" as foo : List U8
|
||||
|
||||
str = Str.fromUtf8 foo |> Result.withDefault ""
|
|
@ -1,5 +1,7 @@
|
|||
interface OneDep
|
||||
exposes [str]
|
||||
imports [Dep3.Blah.{ foo }]
|
||||
imports []
|
||||
|
||||
import Dep3 exposing [foo]
|
||||
|
||||
str = foo
|
|
@ -1,6 +1,11 @@
|
|||
interface Primary
|
||||
exposes [blah2, blah3, str, alwaysThree, identity, z, w, succeed, withDefault, yay]
|
||||
imports [Dep1, Dep2.{ two }, Dep3.Blah.{ bar }, Res]
|
||||
imports []
|
||||
|
||||
import Dep1
|
||||
import Dep2
|
||||
import Dep3 exposing [bar]
|
||||
import Res
|
||||
|
||||
blah2 = Dep2.two
|
||||
blah3 = bar
|
|
@ -1,6 +1,9 @@
|
|||
interface WithBuiltins
|
||||
exposes [floatTest, divisionFn, divisionTest, intTest, constantNum, fromDep2, divDep1ByDep2]
|
||||
imports [Dep1, Dep2.{ two }]
|
||||
imports []
|
||||
|
||||
import Dep1
|
||||
import Dep2
|
||||
|
||||
floatTest = Num.maxF64
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
interface MissingDep
|
||||
exposes [unit]
|
||||
imports [ThisFileIsMissing]
|
||||
imports []
|
||||
|
||||
import ThisFileIsMissing
|
||||
|
||||
Unit : [Unit]
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
interface MissingIngestedFile
|
||||
exposes [unit]
|
||||
imports ["ThisFileIsMissing" as data: List U8]
|
||||
imports []
|
||||
|
||||
import "ThisFileIsMissing" as data : List U8
|
||||
|
||||
Unit : [Unit]
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
use crate::ident::{Ident, ModuleName};
|
||||
use crate::ident::{Ident, Lowercase, ModuleName};
|
||||
use crate::module_err::{IdentIdNotFoundSnafu, ModuleIdNotFoundSnafu, ModuleResult};
|
||||
use roc_collections::{SmallStringInterner, VecMap};
|
||||
use roc_error_macros::internal_error;
|
||||
|
@ -115,6 +115,15 @@ impl Symbol {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn is_automatically_imported(self) -> bool {
|
||||
let module_id = self.module_id();
|
||||
|
||||
module_id.is_automatically_imported()
|
||||
&& Self::builtin_types_in_scope(module_id)
|
||||
.iter()
|
||||
.any(|(_, (s, _))| *s == self)
|
||||
}
|
||||
|
||||
pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName {
|
||||
interns
|
||||
.module_ids
|
||||
|
@ -384,6 +393,11 @@ impl ModuleId {
|
|||
.get_name(self)
|
||||
.unwrap_or_else(|| internal_error!("Could not find ModuleIds for {:?}", self))
|
||||
}
|
||||
|
||||
pub fn is_automatically_imported(self) -> bool {
|
||||
// The deprecated TotallyNotJson module is not automatically imported.
|
||||
self.is_builtin() && self != ModuleId::JSON
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ModuleId {
|
||||
|
@ -448,6 +462,15 @@ impl<'a, T> PackageQualified<'a, T> {
|
|||
PackageQualified::Qualified(_, name) => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_module<B>(&self, f: impl FnOnce(&T) -> B) -> PackageQualified<'a, B> {
|
||||
match self {
|
||||
PackageQualified::Unqualified(name) => PackageQualified::Unqualified(f(name)),
|
||||
PackageQualified::Qualified(package, name) => {
|
||||
PackageQualified::Qualified(package, f(name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -593,6 +616,68 @@ impl ModuleIds {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeModules {
|
||||
modules: VecMap<ModuleName, ModuleId>,
|
||||
sources: VecMap<ModuleId, ScopeModuleSource>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ScopeModuleSource {
|
||||
Builtin,
|
||||
Current,
|
||||
Import(Region),
|
||||
}
|
||||
|
||||
impl ScopeModules {
|
||||
pub fn get_id(&self, module_name: &ModuleName) -> Option<ModuleId> {
|
||||
self.modules.get(module_name).copied()
|
||||
}
|
||||
|
||||
pub fn has_id(&self, module_id: ModuleId) -> bool {
|
||||
self.sources.contains_key(&module_id)
|
||||
}
|
||||
|
||||
pub fn available_names(&self) -> impl Iterator<Item = &ModuleName> {
|
||||
self.modules.keys()
|
||||
}
|
||||
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
module_name: ModuleName,
|
||||
module_id: ModuleId,
|
||||
region: Region,
|
||||
) -> Result<(), ScopeModuleSource> {
|
||||
if let Some(existing_module_id) = self.modules.get(&module_name) {
|
||||
if *existing_module_id == module_id {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
return Err(*self.sources.get(existing_module_id).unwrap());
|
||||
}
|
||||
|
||||
self.modules.insert(module_name, module_id);
|
||||
self.sources
|
||||
.insert(module_id, ScopeModuleSource::Import(region));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
debug_assert_eq!(self.modules.len(), self.sources.len());
|
||||
self.modules.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
debug_assert_eq!(self.modules.is_empty(), self.sources.is_empty());
|
||||
self.modules.is_empty()
|
||||
}
|
||||
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
self.modules.truncate(len);
|
||||
self.sources.truncate(len);
|
||||
}
|
||||
}
|
||||
|
||||
/// An ID that is assigned to interned string identifiers within a module.
|
||||
/// By turning these strings into numbers, post-canonicalization processes
|
||||
/// like unification and optimization can run a lot faster.
|
||||
|
@ -697,6 +782,13 @@ impl IdentIds {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
self.interner.is_empty()
|
||||
}
|
||||
|
||||
pub fn exposed_values(&self) -> Vec<Lowercase> {
|
||||
self.ident_strs()
|
||||
.filter(|(_, ident)| ident.starts_with(|c: char| c.is_lowercase()))
|
||||
.map(|(_, ident)| Lowercase::from(ident))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -926,6 +1018,32 @@ macro_rules! define_builtins {
|
|||
}
|
||||
}
|
||||
|
||||
impl ScopeModules {
|
||||
pub fn new(home_id: ModuleId, home_name: ModuleName) -> Self {
|
||||
// +1 because the user will be compiling at least 1 non-builtin module!
|
||||
let capacity = $total + 1;
|
||||
|
||||
let mut modules = VecMap::with_capacity(capacity);
|
||||
let mut sources = VecMap::with_capacity(capacity);
|
||||
|
||||
modules.insert(home_name, home_id);
|
||||
sources.insert(home_id, ScopeModuleSource::Current);
|
||||
|
||||
let mut insert_both = |id: ModuleId, name_str: &'static str| {
|
||||
let name: ModuleName = name_str.into();
|
||||
|
||||
modules.insert(name, id);
|
||||
sources.insert(id, ScopeModuleSource::Builtin);
|
||||
};
|
||||
|
||||
$(
|
||||
insert_both(ModuleId::$module_const, $module_name);
|
||||
)+
|
||||
|
||||
ScopeModules { modules, sources }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for PackageModuleIds<'a> {
|
||||
fn default() -> Self {
|
||||
// +1 because the user will be compiling at least 1 non-builtin module!
|
||||
|
|
|
@ -452,7 +452,7 @@ enum Match {
|
|||
}
|
||||
|
||||
fn check_for_match(branches: &[Branch]) -> Match {
|
||||
match branches.get(0) {
|
||||
match branches.first() {
|
||||
Some(Branch {
|
||||
goal,
|
||||
patterns,
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
hash::{BuildHasher, Hasher},
|
||||
marker::PhantomData,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{cell::RefCell, hash::BuildHasher, marker::PhantomData, sync::Arc};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
@ -591,9 +586,8 @@ struct LockedGlobalInterner<'a, 'r> {
|
|||
///
|
||||
/// This uses the [default_hasher], so interner maps should also rely on [default_hasher].
|
||||
fn hash<V: std::hash::Hash>(val: V) -> u64 {
|
||||
let mut state = roc_collections::all::BuildHasher::default().build_hasher();
|
||||
val.hash(&mut state);
|
||||
state.finish()
|
||||
let hasher = roc_collections::all::BuildHasher::default();
|
||||
hasher.hash_one(&val)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use bumpalo::Bump;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use roc_parse::{module, module::module_defs, parser::Parser, state::State};
|
||||
use roc_parse::{
|
||||
ast::Defs,
|
||||
module::{self, parse_module_defs},
|
||||
state::State,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn parse_benchmark(c: &mut Criterion) {
|
||||
|
@ -18,11 +22,7 @@ pub fn parse_benchmark(c: &mut Criterion) {
|
|||
let (_actual, state) =
|
||||
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
|
||||
|
||||
let min_indent = 0;
|
||||
let res = module_defs()
|
||||
.parse(&arena, state, min_indent)
|
||||
.map(|tuple| tuple.1)
|
||||
.unwrap();
|
||||
let res = parse_module_defs(&arena, state, Defs::default()).unwrap();
|
||||
|
||||
black_box(res.len());
|
||||
})
|
||||
|
@ -43,11 +43,7 @@ pub fn parse_benchmark(c: &mut Criterion) {
|
|||
let (_actual, state) =
|
||||
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
|
||||
|
||||
let min_indent = 0;
|
||||
let res = module_defs()
|
||||
.parse(&arena, state, min_indent)
|
||||
.map(|tuple| tuple.1)
|
||||
.unwrap();
|
||||
let res = parse_module_defs(&arena, state, Defs::default()).unwrap();
|
||||
|
||||
black_box(res.len());
|
||||
})
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::header::{AppHeader, HostedHeader, InterfaceHeader, PackageHeader, PlatformHeader};
|
||||
use crate::header::{
|
||||
self, AppHeader, HostedHeader, ModuleHeader, ModuleName, PackageHeader, PlatformHeader,
|
||||
};
|
||||
use crate::ident::Accessor;
|
||||
use crate::parser::ESingleQuote;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
|
@ -9,9 +10,10 @@ use bumpalo::Bump;
|
|||
use roc_collections::soa::{EitherIndex, Index, Slice};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
|
||||
use roc_module::ident::QualifiedModuleName;
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Spaces<'a, T> {
|
||||
pub before: &'a [CommentOrNewline<'a>],
|
||||
pub item: T,
|
||||
|
@ -46,6 +48,18 @@ impl<'a, T> Spaced<'a, T> {
|
|||
Spaced::SpaceBefore(next, _spaces) | Spaced::SpaceAfter(next, _spaces) => next.item(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map<U, F: Fn(&T) -> U>(&self, arena: &'a Bump, f: F) -> Spaced<'a, U> {
|
||||
match self {
|
||||
Spaced::Item(item) => Spaced::Item(f(item)),
|
||||
Spaced::SpaceBefore(next, spaces) => {
|
||||
Spaced::SpaceBefore(arena.alloc(next.map(arena, f)), spaces)
|
||||
}
|
||||
Spaced::SpaceAfter(next, spaces) => {
|
||||
Spaced::SpaceAfter(arena.alloc(next.map(arena, f)), spaces)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Debug> Debug for Spaced<'a, T> {
|
||||
|
@ -96,9 +110,143 @@ pub struct Module<'a> {
|
|||
pub header: Header<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Module<'a> {
|
||||
pub fn upgrade_header_imports(self, arena: &'a Bump) -> (Self, Defs<'a>) {
|
||||
let (header, defs) = match self.header {
|
||||
Header::Module(header) => (
|
||||
Header::Module(ModuleHeader {
|
||||
interface_imports: None,
|
||||
..header
|
||||
}),
|
||||
Self::header_imports_to_defs(arena, header.interface_imports),
|
||||
),
|
||||
Header::App(header) => (
|
||||
Header::App(AppHeader {
|
||||
old_imports: None,
|
||||
..header
|
||||
}),
|
||||
Self::header_imports_to_defs(arena, header.old_imports),
|
||||
),
|
||||
Header::Package(_) | Header::Platform(_) | Header::Hosted(_) => {
|
||||
(self.header, Defs::default())
|
||||
}
|
||||
};
|
||||
|
||||
(Module { header, ..self }, defs)
|
||||
}
|
||||
|
||||
pub fn header_imports_to_defs(
|
||||
arena: &'a Bump,
|
||||
imports: Option<
|
||||
header::KeywordItem<'a, header::ImportsKeyword, header::ImportsCollection<'a>>,
|
||||
>,
|
||||
) -> Defs<'a> {
|
||||
let mut defs = Defs::default();
|
||||
|
||||
if let Some(imports) = imports {
|
||||
let len = imports.item.len();
|
||||
|
||||
for (index, import) in imports.item.iter().enumerate() {
|
||||
let spaced = import.extract_spaces();
|
||||
|
||||
let value_def = match spaced.item {
|
||||
header::ImportsEntry::Package(pkg_name, name, exposed) => {
|
||||
Self::header_import_to_value_def(
|
||||
Some(pkg_name),
|
||||
name,
|
||||
exposed,
|
||||
import.region,
|
||||
)
|
||||
}
|
||||
header::ImportsEntry::Module(name, exposed) => {
|
||||
Self::header_import_to_value_def(None, name, exposed, import.region)
|
||||
}
|
||||
header::ImportsEntry::IngestedFile(path, typed_ident) => {
|
||||
ValueDef::IngestedFileImport(IngestedFileImport {
|
||||
before_path: &[],
|
||||
path: Loc {
|
||||
value: path,
|
||||
region: import.region,
|
||||
},
|
||||
name: header::KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: &[],
|
||||
item: ImportAsKeyword,
|
||||
after: &[],
|
||||
},
|
||||
item: Loc {
|
||||
value: typed_ident,
|
||||
region: import.region,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
defs.push_value_def(
|
||||
value_def,
|
||||
import.region,
|
||||
if index == 0 {
|
||||
let mut before = vec![CommentOrNewline::Newline, CommentOrNewline::Newline];
|
||||
before.extend(spaced.before);
|
||||
arena.alloc(before)
|
||||
} else {
|
||||
spaced.before
|
||||
},
|
||||
if index == len - 1 {
|
||||
let mut after = spaced.after.to_vec();
|
||||
after.extend_from_slice(imports.item.final_comments());
|
||||
after.push(CommentOrNewline::Newline);
|
||||
after.push(CommentOrNewline::Newline);
|
||||
arena.alloc(after)
|
||||
} else {
|
||||
spaced.after
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
defs
|
||||
}
|
||||
|
||||
fn header_import_to_value_def(
|
||||
pkg_name: Option<&'a str>,
|
||||
name: header::ModuleName<'a>,
|
||||
exposed: Collection<'a, Loc<Spaced<'a, header::ExposedName<'a>>>>,
|
||||
region: Region,
|
||||
) -> ValueDef<'a> {
|
||||
use crate::header::KeywordItem;
|
||||
|
||||
let new_exposed = if exposed.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: &[],
|
||||
item: ImportExposingKeyword,
|
||||
after: &[],
|
||||
},
|
||||
item: exposed,
|
||||
})
|
||||
};
|
||||
|
||||
ValueDef::ModuleImport(ModuleImport {
|
||||
before_name: &[],
|
||||
name: Loc {
|
||||
region,
|
||||
value: ImportedModuleName {
|
||||
package: pkg_name,
|
||||
name,
|
||||
},
|
||||
},
|
||||
alias: None,
|
||||
exposed: new_exposed,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Header<'a> {
|
||||
Interface(InterfaceHeader<'a>),
|
||||
Module(ModuleHeader<'a>),
|
||||
App(AppHeader<'a>),
|
||||
Package(PackageHeader<'a>),
|
||||
Platform(PlatformHeader<'a>),
|
||||
|
@ -283,9 +431,6 @@ pub enum Expr<'a> {
|
|||
// Record Builders
|
||||
RecordBuilder(Collection<'a, Loc<RecordBuilderField<'a>>>),
|
||||
|
||||
// The name of a file to be ingested directly into a variable.
|
||||
IngestedFile(&'a Path, &'a Loc<TypeAnnotation<'a>>),
|
||||
|
||||
// Lookups
|
||||
Var {
|
||||
module_name: &'a str, // module_name will only be filled if the original Roc code stated something like `5 + SomeModule.myVar`, module_name will be blank if it was `5 + myVar`
|
||||
|
@ -456,7 +601,6 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
|
|||
Expr::RecordBuilder(items) => items
|
||||
.iter()
|
||||
.any(|rbf| is_record_builder_field_suffixed(&rbf.value)),
|
||||
Expr::IngestedFile(_, _) => false,
|
||||
Expr::Underscore(_) => false,
|
||||
Expr::Crash => false,
|
||||
Expr::Tag(_) => false,
|
||||
|
@ -622,10 +766,53 @@ pub enum ValueDef<'a> {
|
|||
preceding_comment: Region,
|
||||
},
|
||||
|
||||
/// e.g. `import InternalHttp as Http exposing [Req]`.
|
||||
ModuleImport(ModuleImport<'a>),
|
||||
|
||||
/// e.g. `import "path/to/my/file.txt" as myFile : Str`
|
||||
IngestedFileImport(IngestedFileImport<'a>),
|
||||
|
||||
Stmt(&'a Loc<Expr<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> ValueDef<'a> {
|
||||
pub fn expr(&self) -> Option<&'a Expr<'a>> {
|
||||
match self {
|
||||
ValueDef::Body(_, body) => Some(&body.value),
|
||||
|
||||
ValueDef::AnnotatedBody {
|
||||
ann_pattern: _,
|
||||
ann_type: _,
|
||||
comment: _,
|
||||
body_pattern: _,
|
||||
body_expr,
|
||||
} => Some(&body_expr.value),
|
||||
|
||||
ValueDef::Dbg {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
}
|
||||
| ValueDef::Expect {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
}
|
||||
| ValueDef::ExpectFx {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => Some(&condition.value),
|
||||
|
||||
ValueDef::Annotation(_, _)
|
||||
| ValueDef::ModuleImport(ModuleImport {
|
||||
before_name: _,
|
||||
name: _,
|
||||
alias: _,
|
||||
exposed: _,
|
||||
})
|
||||
| ValueDef::IngestedFileImport(_) => None,
|
||||
ValueDef::Stmt(loc_expr) => Some(&loc_expr.value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_expr(&mut self, new_expr: &'a Loc<Expr<'a>>) {
|
||||
match self {
|
||||
ValueDef::Body(_, expr) => *expr = new_expr,
|
||||
|
@ -635,6 +822,281 @@ impl<'a> ValueDef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct RecursiveValueDefIter<'a, 'b> {
|
||||
current: &'b Defs<'a>,
|
||||
index: usize,
|
||||
pending: std::vec::Vec<&'b Defs<'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
|
||||
pub fn new(defs: &'b Defs<'a>) -> Self {
|
||||
Self {
|
||||
current: defs,
|
||||
index: 0,
|
||||
pending: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn push_pending_from_expr(&mut self, expr: &'b Expr<'a>) {
|
||||
let mut expr_stack = vec![expr];
|
||||
|
||||
use Expr::*;
|
||||
|
||||
macro_rules! push_stack_from_record_fields {
|
||||
($fields:expr) => {
|
||||
for field in $fields.items {
|
||||
let mut current = field.value;
|
||||
|
||||
loop {
|
||||
use AssignedField::*;
|
||||
|
||||
match current {
|
||||
RequiredValue(_, _, loc_val) => break expr_stack.push(&loc_val.value),
|
||||
OptionalValue(_, _, loc_val) => break expr_stack.push(&loc_val.value),
|
||||
SpaceBefore(next, _) => current = *next,
|
||||
SpaceAfter(next, _) => current = *next,
|
||||
LabelOnly(_) | Malformed(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
while let Some(next) = expr_stack.pop() {
|
||||
match next {
|
||||
Defs(defs, cont) => {
|
||||
self.pending.push(defs);
|
||||
// We purposefully don't push the exprs inside defs here
|
||||
// because they will be traversed when the iterator
|
||||
// gets to their parent def.
|
||||
expr_stack.push(&cont.value);
|
||||
}
|
||||
List(list) => {
|
||||
expr_stack.reserve(list.len());
|
||||
for loc_expr in list.items {
|
||||
expr_stack.push(&loc_expr.value);
|
||||
}
|
||||
}
|
||||
RecordUpdate { update, fields } => {
|
||||
expr_stack.reserve(fields.len() + 1);
|
||||
expr_stack.push(&update.value);
|
||||
push_stack_from_record_fields!(fields);
|
||||
}
|
||||
Record(fields) => {
|
||||
expr_stack.reserve(fields.len());
|
||||
push_stack_from_record_fields!(fields);
|
||||
}
|
||||
Tuple(fields) => {
|
||||
expr_stack.reserve(fields.len());
|
||||
for loc_expr in fields.items {
|
||||
expr_stack.push(&loc_expr.value);
|
||||
}
|
||||
}
|
||||
RecordBuilder(fields) => {
|
||||
expr_stack.reserve(fields.len());
|
||||
for loc_record_builder_field in fields.items {
|
||||
let mut current_field = loc_record_builder_field.value;
|
||||
|
||||
loop {
|
||||
use RecordBuilderField::*;
|
||||
|
||||
match current_field {
|
||||
Value(_, _, loc_val) => break expr_stack.push(&loc_val.value),
|
||||
ApplyValue(_, _, _, loc_val) => {
|
||||
break expr_stack.push(&loc_val.value)
|
||||
}
|
||||
SpaceBefore(next_field, _) => current_field = *next_field,
|
||||
SpaceAfter(next_field, _) => current_field = *next_field,
|
||||
LabelOnly(_) | Malformed(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Closure(_, body) => expr_stack.push(&body.value),
|
||||
Backpassing(_, a, b) => {
|
||||
expr_stack.reserve(2);
|
||||
expr_stack.push(&a.value);
|
||||
expr_stack.push(&b.value);
|
||||
}
|
||||
Expect(condition, cont)
|
||||
| Dbg(condition, cont)
|
||||
| LowLevelDbg(_, condition, cont) => {
|
||||
expr_stack.reserve(2);
|
||||
expr_stack.push(&condition.value);
|
||||
expr_stack.push(&cont.value);
|
||||
}
|
||||
Apply(fun, args, _) => {
|
||||
expr_stack.reserve(args.len() + 1);
|
||||
expr_stack.push(&fun.value);
|
||||
|
||||
for loc_expr in args.iter() {
|
||||
expr_stack.push(&loc_expr.value);
|
||||
}
|
||||
}
|
||||
BinOps(ops, expr) => {
|
||||
expr_stack.reserve(ops.len() + 1);
|
||||
|
||||
for (a, _) in ops.iter() {
|
||||
expr_stack.push(&a.value);
|
||||
}
|
||||
expr_stack.push(&expr.value);
|
||||
}
|
||||
UnaryOp(expr, _) => expr_stack.push(&expr.value),
|
||||
If(ifs, alternate) => {
|
||||
expr_stack.reserve(ifs.len() * 2 + 1);
|
||||
|
||||
for (condition, consequent) in ifs.iter() {
|
||||
expr_stack.push(&condition.value);
|
||||
expr_stack.push(&consequent.value);
|
||||
}
|
||||
expr_stack.push(&alternate.value);
|
||||
}
|
||||
When(condition, branches) => {
|
||||
expr_stack.reserve(branches.len() + 1);
|
||||
expr_stack.push(&condition.value);
|
||||
|
||||
for WhenBranch {
|
||||
patterns: _,
|
||||
value,
|
||||
guard,
|
||||
} in branches.iter()
|
||||
{
|
||||
expr_stack.push(&value.value);
|
||||
|
||||
match guard {
|
||||
None => {}
|
||||
Some(guard) => expr_stack.push(&guard.value),
|
||||
}
|
||||
}
|
||||
}
|
||||
RecordAccess(expr, _)
|
||||
| TupleAccess(expr, _)
|
||||
| TaskAwaitBang(expr)
|
||||
| SpaceBefore(expr, _)
|
||||
| SpaceAfter(expr, _)
|
||||
| ParensAround(expr) => expr_stack.push(expr),
|
||||
|
||||
MultipleRecordBuilders(loc_expr) | UnappliedRecordBuilder(loc_expr) => {
|
||||
expr_stack.push(&loc_expr.value)
|
||||
}
|
||||
|
||||
Float(_)
|
||||
| Num(_)
|
||||
| NonBase10Int { .. }
|
||||
| Str(_)
|
||||
| SingleQuote(_)
|
||||
| AccessorFunction(_)
|
||||
| Var { .. }
|
||||
| Underscore(_)
|
||||
| Crash
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| MalformedIdent(_, _)
|
||||
| MalformedClosure
|
||||
| PrecedenceConflict(_)
|
||||
| MalformedSuffixed(_)
|
||||
| EmptyDefsFinal => { /* terminal */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> {
|
||||
type Item = (&'b ValueDef<'a>, &'b Region);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.current.tags.get(self.index) {
|
||||
Some(tag) => {
|
||||
if let Err(def_index) = tag.split() {
|
||||
let def = &self.current.value_defs[def_index.index()];
|
||||
let region = &self.current.regions[self.index];
|
||||
|
||||
if let Some(expr) = def.expr() {
|
||||
self.push_pending_from_expr(expr);
|
||||
}
|
||||
|
||||
self.index += 1;
|
||||
|
||||
Some((def, region))
|
||||
} else {
|
||||
// Not a value def, try next
|
||||
self.index += 1;
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
self.current = self.pending.pop()?;
|
||||
self.index = 0;
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ModuleImport<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<ImportedModuleName<'a>>,
|
||||
pub alias: Option<header::KeywordItem<'a, ImportAsKeyword, Loc<ImportAlias<'a>>>>,
|
||||
pub exposed: Option<
|
||||
header::KeywordItem<
|
||||
'a,
|
||||
ImportExposingKeyword,
|
||||
Collection<'a, Loc<Spaced<'a, header::ExposedName<'a>>>>,
|
||||
>,
|
||||
>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct IngestedFileImport<'a> {
|
||||
pub before_path: &'a [CommentOrNewline<'a>],
|
||||
pub path: Loc<StrLiteral<'a>>,
|
||||
pub name: header::KeywordItem<'a, ImportAsKeyword, Loc<Spaced<'a, header::TypedIdent<'a>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ImportAsKeyword;
|
||||
|
||||
impl header::Keyword for ImportAsKeyword {
|
||||
const KEYWORD: &'static str = "as";
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ImportExposingKeyword;
|
||||
|
||||
impl header::Keyword for ImportExposingKeyword {
|
||||
const KEYWORD: &'static str = "exposing";
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ImportedModuleName<'a> {
|
||||
pub package: Option<&'a str>,
|
||||
pub name: ModuleName<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<ImportedModuleName<'a>> for QualifiedModuleName<'a> {
|
||||
fn from(imported: ImportedModuleName<'a>) -> Self {
|
||||
Self {
|
||||
opt_package: imported.package,
|
||||
module: imported.name.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ImportAlias<'a>(&'a str);
|
||||
|
||||
impl<'a> ImportAlias<'a> {
|
||||
pub const fn new(name: &'a str) -> Self {
|
||||
ImportAlias(name)
|
||||
}
|
||||
|
||||
pub const fn as_str(&'a self) -> &'a str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct Defs<'a> {
|
||||
pub tags: std::vec::Vec<EitherIndex<TypeDef<'a>, ValueDef<'a>>>,
|
||||
|
@ -1847,7 +2309,7 @@ impl<'a> Malformed for Module<'a> {
|
|||
impl<'a> Malformed for Header<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
Header::Interface(header) => header.is_malformed(),
|
||||
Header::Module(header) => header.is_malformed(),
|
||||
Header::App(header) => header.is_malformed(),
|
||||
Header::Package(header) => header.is_malformed(),
|
||||
Header::Platform(header) => header.is_malformed(),
|
||||
|
@ -1876,7 +2338,6 @@ impl<'a> Malformed for Expr<'a> {
|
|||
Tag(_) |
|
||||
OpaqueRef(_) |
|
||||
SingleQuote(_) | // This is just a &str - not a bunch of segments
|
||||
IngestedFile(_, _) |
|
||||
EmptyDefsFinal |
|
||||
Crash => false,
|
||||
|
||||
|
@ -2150,6 +2611,17 @@ impl<'a> Malformed for ValueDef<'a> {
|
|||
condition,
|
||||
preceding_comment: _,
|
||||
} => condition.is_malformed(),
|
||||
ValueDef::ModuleImport(ModuleImport {
|
||||
before_name: _,
|
||||
name: _,
|
||||
alias: _,
|
||||
exposed: _,
|
||||
}) => false,
|
||||
ValueDef::IngestedFileImport(IngestedFileImport {
|
||||
before_path: _,
|
||||
path,
|
||||
name: _,
|
||||
}) => path.is_malformed(),
|
||||
ValueDef::Stmt(loc_expr) => loc_expr.is_malformed(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -322,43 +322,6 @@ pub fn fast_eat_until_control_character(bytes: &[u8]) -> usize {
|
|||
simple_eat_until_control_character(&bytes[i..]) + i
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_eat_whitespace_simple() {
|
||||
let bytes = &[0, 0, 0, 0, 0, 0, 0, 0];
|
||||
assert_eq!(simple_eat_whitespace(bytes), fast_eat_whitespace(bytes));
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_eat_whitespace(bytes in proptest::collection::vec(any::<u8>(), 0..100)) {
|
||||
prop_assert_eq!(simple_eat_whitespace(&bytes), fast_eat_whitespace(&bytes));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eat_until_control_character_simple() {
|
||||
let bytes = &[32, 0, 0, 0, 0, 0, 0, 0];
|
||||
assert_eq!(
|
||||
simple_eat_until_control_character(bytes),
|
||||
fast_eat_until_control_character(bytes)
|
||||
);
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_eat_until_control_character(bytes in proptest::collection::vec(any::<u8>(), 0..100)) {
|
||||
prop_assert_eq!(
|
||||
simple_eat_until_control_character(&bytes),
|
||||
fast_eat_until_control_character(&bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn space0_e<'a, E>(
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
||||
|
@ -514,3 +477,40 @@ where
|
|||
|
||||
Ok((progress, state))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_eat_whitespace_simple() {
|
||||
let bytes = &[0, 0, 0, 0, 0, 0, 0, 0];
|
||||
assert_eq!(simple_eat_whitespace(bytes), fast_eat_whitespace(bytes));
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_eat_whitespace(bytes in proptest::collection::vec(any::<u8>(), 0..100)) {
|
||||
prop_assert_eq!(simple_eat_whitespace(&bytes), fast_eat_whitespace(&bytes));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eat_until_control_character_simple() {
|
||||
let bytes = &[32, 0, 0, 0, 0, 0, 0, 0];
|
||||
assert_eq!(
|
||||
simple_eat_until_control_character(bytes),
|
||||
fast_eat_until_control_character(bytes)
|
||||
);
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_eat_until_control_character(bytes in proptest::collection::vec(any::<u8>(), 0..100)) {
|
||||
prop_assert_eq!(
|
||||
simple_eat_until_control_character(&bytes),
|
||||
fast_eat_until_control_character(&bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
use crate::ast::{
|
||||
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
|
||||
Implements, ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
||||
ImportedModuleName, IngestedFileImport, ModuleImport, Pattern, RecordBuilderField, Spaceable,
|
||||
Spaced, Spaces, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
||||
space0_before_optional_after, space0_e, spaces, spaces_around, spaces_before,
|
||||
};
|
||||
use crate::ident::{integer_ident, lowercase_ident, parse_ident, Accessor, Ident, Suffix};
|
||||
use crate::keyword;
|
||||
use crate::ident::{
|
||||
integer_ident, lowercase_ident, parse_ident, unqualified_ident, uppercase_ident, Accessor,
|
||||
Ident, Suffix,
|
||||
};
|
||||
use crate::module::module_name_help;
|
||||
use crate::parser::{
|
||||
self, backtrackable, byte, byte_indent, increment_min_indent, line_min_indent, optional,
|
||||
reset_min_indent, sep_by1, sep_by1_e, set_min_indent, specialize_err, specialize_err_ref, then,
|
||||
two_bytes, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern, ERecord,
|
||||
EString, EType, EWhen, Either, ParseResult, Parser,
|
||||
two_bytes, EClosure, EExpect, EExpr, EIf, EImport, EInParens, EList, ENumber, EPattern,
|
||||
ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
|
||||
};
|
||||
use crate::pattern::{closure_param, loc_implements_parser};
|
||||
use crate::state::State;
|
||||
use crate::string_literal::StrLikeLiteral;
|
||||
use crate::type_annotation;
|
||||
use crate::string_literal::{self, StrLikeLiteral};
|
||||
use crate::{header, keyword};
|
||||
use crate::{module, type_annotation};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::soa::Slice;
|
||||
|
@ -286,17 +291,19 @@ fn crash_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
|||
fn loc_possibly_negative_or_negated_term<'a>(
|
||||
options: ExprParseOptions,
|
||||
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
let parse_unary_negate = move |arena, state: State<'a>, min_indent: u32| {
|
||||
let initial = state.clone();
|
||||
|
||||
let (_, (loc_op, loc_expr), state) =
|
||||
and!(loc!(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
|
||||
|
||||
let loc_expr = numeric_negate_expression(arena, initial, loc_op, loc_expr, &[]);
|
||||
|
||||
Ok((MadeProgress, loc_expr, state))
|
||||
};
|
||||
|
||||
one_of![
|
||||
|arena, state: State<'a>, min_indent: u32| {
|
||||
let initial = state.clone();
|
||||
|
||||
let (_, (loc_op, loc_expr), state) =
|
||||
and!(loc!(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
|
||||
|
||||
let loc_expr = numeric_negate_expression(arena, initial, loc_op, loc_expr, &[]);
|
||||
|
||||
Ok((MadeProgress, loc_expr, state))
|
||||
},
|
||||
parse_unary_negate,
|
||||
// this will parse negative numbers, which the unary negate thing up top doesn't (for now)
|
||||
loc!(specialize_err(EExpr::Number, number_literal_help())),
|
||||
loc!(map_with_arena!(
|
||||
|
@ -345,6 +352,7 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, E
|
|||
loc!(specialize_err(EExpr::When, when::expr_help(options))),
|
||||
loc!(specialize_err(EExpr::Expect, expect_help(options))),
|
||||
loc!(specialize_err(EExpr::Dbg, dbg_help(options))),
|
||||
loc!(import_help(options)),
|
||||
loc!(specialize_err(EExpr::Closure, closure_help(options))),
|
||||
loc!(expr_operator_chain(options)),
|
||||
fail_expr_start_e()
|
||||
|
@ -648,30 +656,43 @@ pub fn parse_single_def<'a>(
|
|||
min_indent,
|
||||
) {
|
||||
Err((NoProgress, _)) => {
|
||||
match parse_expect.parse(arena, state.clone(), min_indent) {
|
||||
match loc!(import()).parse(arena, state.clone(), min_indent) {
|
||||
Err((_, _)) => {
|
||||
// a hacky way to get expression-based error messages. TODO fix this
|
||||
Ok((NoProgress, None, initial))
|
||||
match parse_expect.parse(arena, state.clone(), min_indent) {
|
||||
Err((_, _)) => {
|
||||
// a hacky way to get expression-based error messages. TODO fix this
|
||||
Ok((NoProgress, None, initial))
|
||||
}
|
||||
Ok((_, expect_flavor, state)) => parse_statement_inside_def(
|
||||
arena,
|
||||
state,
|
||||
min_indent,
|
||||
options,
|
||||
start,
|
||||
spaces_before_current_start,
|
||||
spaces_before_current,
|
||||
|preceding_comment, loc_def_expr| match expect_flavor {
|
||||
Either::Second(_) => ValueDef::Expect {
|
||||
condition: arena.alloc(loc_def_expr),
|
||||
preceding_comment,
|
||||
},
|
||||
Either::First(_) => ValueDef::ExpectFx {
|
||||
condition: arena.alloc(loc_def_expr),
|
||||
preceding_comment,
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok((_, expect_flavor, state)) => parse_statement_inside_def(
|
||||
arena,
|
||||
Ok((_, loc_import, state)) => Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(loc_import.value),
|
||||
region: loc_import.region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
min_indent,
|
||||
options,
|
||||
start,
|
||||
spaces_before_current_start,
|
||||
spaces_before_current,
|
||||
|preceding_comment, loc_def_expr| match expect_flavor {
|
||||
Either::Second(_) => ValueDef::Expect {
|
||||
condition: arena.alloc(loc_def_expr),
|
||||
preceding_comment,
|
||||
},
|
||||
Either::First(_) => ValueDef::ExpectFx {
|
||||
condition: arena.alloc(loc_def_expr),
|
||||
preceding_comment,
|
||||
},
|
||||
},
|
||||
),
|
||||
)),
|
||||
}
|
||||
}
|
||||
Err((MadeProgress, _)) => {
|
||||
|
@ -936,6 +957,121 @@ pub fn parse_single_def<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
|
||||
skip_first!(
|
||||
parser::keyword(keyword::IMPORT, EImport::Import),
|
||||
increment_min_indent(one_of!(import_body(), import_ingested_file_body()))
|
||||
)
|
||||
}
|
||||
|
||||
fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
|
||||
map!(
|
||||
record!(ModuleImport {
|
||||
before_name: space0_e(EImport::IndentStart),
|
||||
name: loc!(imported_module_name()),
|
||||
alias: optional(backtrackable(import_as())),
|
||||
exposed: optional(backtrackable(import_exposing()))
|
||||
}),
|
||||
ValueDef::ModuleImport
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport<'a>> {
|
||||
record!(ImportedModuleName {
|
||||
package: optional(skip_second!(
|
||||
specialize_err(|_, pos| EImport::PackageShorthand(pos), lowercase_ident()),
|
||||
byte(b'.', EImport::PackageShorthandDot)
|
||||
)),
|
||||
name: module_name_help(EImport::ModuleName)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn import_as<'a>(
|
||||
) -> impl Parser<'a, header::KeywordItem<'a, ImportAsKeyword, Loc<ImportAlias<'a>>>, EImport<'a>> {
|
||||
record!(header::KeywordItem {
|
||||
keyword: module::spaces_around_keyword(
|
||||
ImportAsKeyword,
|
||||
EImport::As,
|
||||
EImport::IndentAs,
|
||||
EImport::IndentAlias
|
||||
),
|
||||
item: loc!(map!(
|
||||
specialize_err(|_, pos| EImport::Alias(pos), uppercase_ident()),
|
||||
ImportAlias::new
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn import_exposing<'a>() -> impl Parser<
|
||||
'a,
|
||||
header::KeywordItem<
|
||||
'a,
|
||||
ImportExposingKeyword,
|
||||
Collection<'a, Loc<Spaced<'a, header::ExposedName<'a>>>>,
|
||||
>,
|
||||
EImport<'a>,
|
||||
> {
|
||||
record!(header::KeywordItem {
|
||||
keyword: module::spaces_around_keyword(
|
||||
ImportExposingKeyword,
|
||||
EImport::Exposing,
|
||||
EImport::IndentExposing,
|
||||
EImport::ExposingListStart,
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
byte(b'[', EImport::ExposingListStart),
|
||||
loc!(import_exposed_name()),
|
||||
byte(b',', EImport::ExposingListEnd),
|
||||
byte(b']', EImport::ExposingListEnd),
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn import_exposed_name<'a>(
|
||||
) -> impl Parser<'a, crate::ast::Spaced<'a, crate::header::ExposedName<'a>>, EImport<'a>> {
|
||||
map!(
|
||||
specialize_err(|_, pos| EImport::ExposedName(pos), unqualified_ident()),
|
||||
|n| Spaced::Item(crate::header::ExposedName::new(n))
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn import_ingested_file_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
|
||||
map!(
|
||||
record!(IngestedFileImport {
|
||||
before_path: space0_e(EImport::IndentStart),
|
||||
path: loc!(specialize_err(
|
||||
|_, pos| EImport::IngestedPath(pos),
|
||||
string_literal::parse_str_literal()
|
||||
)),
|
||||
name: import_ingested_file_as(),
|
||||
}),
|
||||
ValueDef::IngestedFileImport
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn import_ingested_file_as<'a>() -> impl Parser<
|
||||
'a,
|
||||
header::KeywordItem<'a, ImportAsKeyword, Loc<Spaced<'a, header::TypedIdent<'a>>>>,
|
||||
EImport<'a>,
|
||||
> {
|
||||
record!(header::KeywordItem {
|
||||
keyword: module::spaces_around_keyword(
|
||||
ImportAsKeyword,
|
||||
EImport::As,
|
||||
EImport::IndentAs,
|
||||
EImport::IndentIngestedName
|
||||
),
|
||||
item: specialize_err(EImport::IngestedName, loc!(module::typed_ident()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_single_def_assignment<'a>(
|
||||
options: ExprParseOptions,
|
||||
min_indent: u32,
|
||||
|
@ -2243,8 +2379,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
|||
is_negative,
|
||||
},
|
||||
// These would not have parsed as patterns
|
||||
Expr::IngestedFile(_, _)
|
||||
| Expr::AccessorFunction(_)
|
||||
Expr::AccessorFunction(_)
|
||||
| Expr::RecordAccess(_, _)
|
||||
| Expr::TupleAccess(_, _)
|
||||
| Expr::List { .. }
|
||||
|
@ -2332,41 +2467,42 @@ fn assigned_expr_field_to_pattern_help<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn toplevel_defs<'a>() -> impl Parser<'a, Defs<'a>, EExpr<'a>> {
|
||||
move |arena, state: State<'a>, min_indent: u32| {
|
||||
let (_, initial_space, state) =
|
||||
space0_e(EExpr::IndentEnd).parse(arena, state, min_indent)?;
|
||||
pub fn parse_top_level_defs<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
state: State<'a>,
|
||||
mut output: Defs<'a>,
|
||||
) -> ParseResult<'a, Defs<'a>, EExpr<'a>> {
|
||||
let (_, initial_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, 0)?;
|
||||
|
||||
let start_column = state.column();
|
||||
let start_column = state.column();
|
||||
|
||||
let options = ExprParseOptions {
|
||||
accept_multi_backpassing: true,
|
||||
check_for_arrow: true,
|
||||
suffixed_found: false,
|
||||
};
|
||||
let options = ExprParseOptions {
|
||||
accept_multi_backpassing: true,
|
||||
check_for_arrow: true,
|
||||
suffixed_found: false,
|
||||
};
|
||||
|
||||
let mut output = Defs::default();
|
||||
let before = Slice::extend_new(&mut output.spaces, initial_space.iter().copied());
|
||||
let existing_len = output.tags.len();
|
||||
|
||||
let (_, mut output, state) = parse_defs_end(options, start_column, output, arena, state)?;
|
||||
let before = Slice::extend_new(&mut output.spaces, initial_space.iter().copied());
|
||||
|
||||
let (_, final_space, state) =
|
||||
space0_e(EExpr::IndentEnd).parse(arena, state, start_column)?;
|
||||
let (_, mut output, state) = parse_defs_end(options, start_column, output, arena, state)?;
|
||||
|
||||
if !output.tags.is_empty() {
|
||||
// add surrounding whitespace
|
||||
let after = Slice::extend_new(&mut output.spaces, final_space.iter().copied());
|
||||
let (_, final_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, start_column)?;
|
||||
|
||||
debug_assert!(output.space_before[0].is_empty());
|
||||
output.space_before[0] = before;
|
||||
if output.tags.len() > existing_len {
|
||||
// add surrounding whitespace
|
||||
let after = Slice::extend_new(&mut output.spaces, final_space.iter().copied());
|
||||
|
||||
let last = output.tags.len() - 1;
|
||||
debug_assert!(output.space_after[last].is_empty() || after.is_empty());
|
||||
output.space_after[last] = after;
|
||||
}
|
||||
debug_assert!(output.space_before[existing_len].is_empty());
|
||||
output.space_before[existing_len] = before;
|
||||
|
||||
Ok((MadeProgress, output, state))
|
||||
let last = output.tags.len() - 1;
|
||||
debug_assert!(output.space_after[last].is_empty() || after.is_empty());
|
||||
output.space_after[last] = after;
|
||||
}
|
||||
|
||||
Ok((MadeProgress, output, state))
|
||||
}
|
||||
|
||||
// PARSER HELPERS
|
||||
|
@ -2375,7 +2511,7 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo
|
|||
// closure_help_help(options)
|
||||
map_with_arena!(
|
||||
// After the first token, all other tokens must be indented past the start of the line
|
||||
indented_seq!(
|
||||
indented_seq_skip_first!(
|
||||
// All closures start with a '\' - e.g. (\x -> x + 1)
|
||||
byte_indent(b'\\', EClosure::Start),
|
||||
// Once we see the '\', we're committed to parsing this as a closure.
|
||||
|
@ -2419,7 +2555,7 @@ mod when {
|
|||
pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
indented_seq!(
|
||||
indented_seq_skip_first!(
|
||||
parser::keyword(keyword::WHEN, EWhen::When),
|
||||
space0_around_e_no_after_indent_check(
|
||||
specialize_err_ref(EWhen::Condition, expr_start(options)),
|
||||
|
@ -2430,7 +2566,7 @@ mod when {
|
|||
// ambiguity. The formatter will fix it up.
|
||||
//
|
||||
// We require that branches are indented relative to the line containing the `is`.
|
||||
indented_seq!(
|
||||
indented_seq_skip_first!(
|
||||
parser::keyword(keyword::IS, EWhen::Is),
|
||||
branches(options)
|
||||
)
|
||||
|
@ -2722,6 +2858,18 @@ fn dbg_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<
|
|||
}
|
||||
}
|
||||
|
||||
fn import_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
|
||||
let (_, import_def, state) =
|
||||
loc!(specialize_err(EExpr::Import, import())).parse(arena, state, min_indent)?;
|
||||
|
||||
let mut defs = Defs::default();
|
||||
defs.push_value_def(import_def.value, import_def.region, &[], &[]);
|
||||
|
||||
parse_defs_expr(options, min_indent, defs, arena, state)
|
||||
}
|
||||
}
|
||||
|
||||
fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<'a>> {
|
||||
move |arena: &'a Bump, state, min_indent| {
|
||||
let (_, _, state) =
|
||||
|
|
|
@ -19,7 +19,7 @@ impl<'a> HeaderType<'a> {
|
|||
}
|
||||
| HeaderType::Hosted { exposes, .. }
|
||||
| HeaderType::Builtin { exposes, .. }
|
||||
| HeaderType::Interface { exposes, .. } => exposes,
|
||||
| HeaderType::Module { exposes, .. } => exposes,
|
||||
HeaderType::Platform { .. } | HeaderType::Package { .. } => &[],
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ impl<'a> HeaderType<'a> {
|
|||
HeaderType::Builtin { .. } => "builtin",
|
||||
HeaderType::Package { .. } => "package",
|
||||
HeaderType::Platform { .. } => "platform",
|
||||
HeaderType::Interface { .. } => "interface",
|
||||
HeaderType::Module { .. } => "module",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ impl<'a> HeaderType<'a> {
|
|||
#[derive(Debug)]
|
||||
pub enum HeaderType<'a> {
|
||||
App {
|
||||
output_name: StrLiteral<'a>,
|
||||
provides: &'a [Loc<ExposedName<'a>>],
|
||||
to_platform: To<'a>,
|
||||
},
|
||||
|
@ -73,7 +72,7 @@ pub enum HeaderType<'a> {
|
|||
/// usually `pf`
|
||||
config_shorthand: &'a str,
|
||||
},
|
||||
Interface {
|
||||
Module {
|
||||
name: ModuleName<'a>,
|
||||
exposes: &'a [Loc<ExposedName<'a>>],
|
||||
},
|
||||
|
@ -82,14 +81,10 @@ pub enum HeaderType<'a> {
|
|||
impl<'a> HeaderType<'a> {
|
||||
pub fn get_name(self) -> Option<&'a str> {
|
||||
match self {
|
||||
Self::Interface { name, .. }
|
||||
| Self::Builtin { name, .. }
|
||||
| Self::Hosted { name, .. } => Some(name.into()),
|
||||
Self::App {
|
||||
output_name: StrLiteral::PlainLine(name),
|
||||
..
|
||||
Self::Module { name, .. } | Self::Builtin { name, .. } | Self::Hosted { name, .. } => {
|
||||
Some(name.into())
|
||||
}
|
||||
| Self::Platform {
|
||||
Self::Platform {
|
||||
config_shorthand: name,
|
||||
..
|
||||
}
|
||||
|
@ -103,6 +98,17 @@ impl<'a> HeaderType<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_maybe_builtin(self, module_id: ModuleId) -> Self {
|
||||
match self {
|
||||
HeaderType::Module { name, exposes } if module_id.is_builtin() => HeaderType::Builtin {
|
||||
name,
|
||||
exposes,
|
||||
generates_with: &[],
|
||||
},
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
|
@ -156,7 +162,15 @@ impl<'a> From<ModuleName<'a>> for &'a str {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<ModuleName<'a>> for roc_module::ident::ModuleName {
|
||||
fn from(name: ModuleName<'a>) -> Self {
|
||||
name.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ModuleName<'a> {
|
||||
const MODULE_SEPARATOR: char = '.';
|
||||
|
||||
pub const fn new(name: &'a str) -> Self {
|
||||
ModuleName(name)
|
||||
}
|
||||
|
@ -164,6 +178,10 @@ impl<'a> ModuleName<'a> {
|
|||
pub const fn as_str(&'a self) -> &'a str {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn parts(&'a self) -> impl DoubleEndedIterator<Item = &'a str> {
|
||||
self.0.split(Self::MODULE_SEPARATOR)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
|
@ -204,7 +222,6 @@ macro_rules! keywords {
|
|||
|
||||
keywords! {
|
||||
ExposesKeyword => "exposes",
|
||||
ImportsKeyword => "imports",
|
||||
WithKeyword => "with",
|
||||
GeneratesKeyword => "generates",
|
||||
PackageKeyword => "package",
|
||||
|
@ -212,23 +229,29 @@ keywords! {
|
|||
RequiresKeyword => "requires",
|
||||
ProvidesKeyword => "provides",
|
||||
ToKeyword => "to",
|
||||
PlatformKeyword => "platform",
|
||||
// Deprecated
|
||||
ImportsKeyword => "imports",
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct KeywordItem<'a, K, V> {
|
||||
pub keyword: Spaces<'a, K>,
|
||||
pub item: V,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub struct ModuleHeader<'a> {
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
|
||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
|
||||
// Keeping this so we can format old interface header into module headers
|
||||
pub interface_imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
|
||||
}
|
||||
|
||||
pub type ImportsKeywordItem<'a> = KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>;
|
||||
pub type ImportsCollection<'a> = Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HostedHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
|
@ -250,14 +273,13 @@ pub enum To<'a> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<StrLiteral<'a>>,
|
||||
|
||||
pub packages:
|
||||
Option<KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>>,
|
||||
pub imports:
|
||||
Option<KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>>,
|
||||
pub provides: ProvidesTo<'a>,
|
||||
pub before_provides: &'a [CommentOrNewline<'a>],
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub packages: Loc<Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||
// Old header pieces
|
||||
pub old_imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
|
||||
pub old_provides_to_new_package: Option<PackageName<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -272,12 +294,10 @@ pub struct ProvidesTo<'a> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PackageHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
|
||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||
pub packages:
|
||||
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub packages: Loc<Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -333,6 +353,7 @@ pub struct TypedIdent<'a> {
|
|||
pub struct PackageEntry<'a> {
|
||||
pub shorthand: &'a str,
|
||||
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||
pub platform_marker: Option<&'a [CommentOrNewline<'a>]>,
|
||||
pub package_name: Loc<PackageName<'a>>,
|
||||
}
|
||||
|
||||
|
@ -353,9 +374,15 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
|
|||
),
|
||||
space0_e(EPackageEntry::IndentPackage)
|
||||
)),
|
||||
loc!(specialize_err(EPackageEntry::BadPackage, package_name()))
|
||||
and!(
|
||||
optional(skip_first!(
|
||||
crate::parser::keyword(crate::keyword::PLATFORM, EPackageEntry::Platform),
|
||||
space0_e(EPackageEntry::IndentPackage)
|
||||
)),
|
||||
loc!(specialize_err(EPackageEntry::BadPackage, package_name()))
|
||||
)
|
||||
),
|
||||
move |arena, (opt_shorthand, package_or_path)| {
|
||||
move |arena, (opt_shorthand, (platform_marker, package_or_path))| {
|
||||
let entry = match opt_shorthand {
|
||||
Some(((shorthand, spaces_before_colon), spaces_after_colon)) => PackageEntry {
|
||||
shorthand,
|
||||
|
@ -364,11 +391,13 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
|
|||
spaces_before_colon,
|
||||
spaces_after_colon,
|
||||
),
|
||||
platform_marker,
|
||||
package_name: package_or_path,
|
||||
},
|
||||
None => PackageEntry {
|
||||
shorthand: "",
|
||||
spaces_after_shorthand: &[],
|
||||
platform_marker,
|
||||
package_name: package_or_path,
|
||||
},
|
||||
};
|
||||
|
@ -402,7 +431,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for InterfaceHeader<'a> {
|
||||
impl<'a> Malformed for ModuleHeader<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
@ -416,7 +445,7 @@ impl<'a> Malformed for HostedHeader<'a> {
|
|||
|
||||
impl<'a> Malformed for AppHeader<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
self.name.is_malformed()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,21 @@ pub const WHEN: &str = "when";
|
|||
pub const AS: &str = "as";
|
||||
pub const IS: &str = "is";
|
||||
pub const DBG: &str = "dbg";
|
||||
pub const IMPORT: &str = "import";
|
||||
pub const EXPECT: &str = "expect";
|
||||
pub const EXPECT_FX: &str = "expect-fx";
|
||||
pub const CRASH: &str = "crash";
|
||||
|
||||
// These keywords are valid in imports
|
||||
pub const EXPOSING: &str = "exposing";
|
||||
|
||||
// These keywords are valid in types
|
||||
pub const IMPLEMENTS: &str = "implements";
|
||||
pub const WHERE: &str = "where";
|
||||
|
||||
pub const KEYWORDS: [&str; 10] = [IF, THEN, ELSE, WHEN, AS, IS, DBG, EXPECT, EXPECT_FX, CRASH];
|
||||
// These keywords are valid in headers
|
||||
pub const PLATFORM: &str = "platform";
|
||||
|
||||
pub const KEYWORDS: [&str; 11] = [
|
||||
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, EXPECT_FX, CRASH,
|
||||
];
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces};
|
||||
use crate::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spaces};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::expr::merge_spaces;
|
||||
use crate::header::{
|
||||
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
||||
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
|
||||
PackageEntry, PackageHeader, PackagesKeyword, PlatformHeader, PlatformRequires,
|
||||
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
HostedHeader, ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword,
|
||||
KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackagesKeyword,
|
||||
PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword,
|
||||
TypedIdent, WithKeyword,
|
||||
};
|
||||
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
||||
use crate::parser::Progress::{self, *};
|
||||
|
@ -16,7 +18,7 @@ use crate::parser::{
|
|||
use crate::state::State;
|
||||
use crate::string_literal::{self, parse_str_literal};
|
||||
use crate::type_annotation;
|
||||
use roc_region::all::{Loc, Position};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
||||
|_arena, state: State<'a>, _min_indent: u32| {
|
||||
|
@ -28,12 +30,19 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn module_defs<'a>() -> impl Parser<'a, Defs<'a>, SyntaxError<'a>> {
|
||||
skip_second!(
|
||||
specialize_err(SyntaxError::Expr, crate::expr::toplevel_defs(),),
|
||||
end_of_file()
|
||||
)
|
||||
pub fn parse_module_defs<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
state: State<'a>,
|
||||
defs: Defs<'a>,
|
||||
) -> Result<Defs<'a>, SyntaxError<'a>> {
|
||||
let min_indent = 0;
|
||||
match crate::expr::parse_top_level_defs(arena, state.clone(), defs) {
|
||||
Ok((_, defs, state)) => match end_of_file().parse(arena, state, min_indent) {
|
||||
Ok(_) => Ok(defs),
|
||||
Err((_, fail)) => Err(fail),
|
||||
},
|
||||
Err((_, fail)) => Err(SyntaxError::Expr(fail, state.pos())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_header<'a>(
|
||||
|
@ -53,24 +62,31 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
|||
record!(Module {
|
||||
comments: space0_e(EHeader::IndentStart),
|
||||
header: one_of![
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword("module", EHeader::Start),
|
||||
increment_min_indent(module_header())
|
||||
),
|
||||
Header::Module
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword("interface", EHeader::Start),
|
||||
increment_min_indent(interface_header())
|
||||
),
|
||||
Header::Interface
|
||||
Header::Module
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword("app", EHeader::Start),
|
||||
increment_min_indent(app_header())
|
||||
increment_min_indent(one_of![app_header(), old_app_header()])
|
||||
),
|
||||
Header::App
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword("package", EHeader::Start),
|
||||
increment_min_indent(package_header())
|
||||
increment_min_indent(one_of![package_header(), old_package_header()])
|
||||
),
|
||||
Header::Package
|
||||
),
|
||||
|
@ -93,22 +109,68 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> {
|
||||
record!(InterfaceHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(module_name_help(EHeader::ModuleName)),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_values()),
|
||||
imports: specialize_err(EHeader::Imports, imports()),
|
||||
fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
|
||||
record!(ModuleHeader {
|
||||
before_exposes: space0_e(EHeader::IndentStart),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_list()),
|
||||
interface_imports: succeed!(None)
|
||||
})
|
||||
.trace("module_header")
|
||||
}
|
||||
|
||||
macro_rules! merge_n_spaces {
|
||||
($arena:expr, $($slice:expr),*) => {
|
||||
{
|
||||
let mut merged = bumpalo::collections::Vec::with_capacity_in(0 $(+ $slice.len())*, $arena);
|
||||
$(merged.extend_from_slice($slice);)*
|
||||
merged.into_bump_slice()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Parse old interface headers so we can format them into module headers
|
||||
#[inline(always)]
|
||||
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
|
||||
let before_exposes = map_with_arena!(
|
||||
and!(
|
||||
skip_second!(
|
||||
space0_e(EHeader::IndentStart),
|
||||
loc!(module_name_help(EHeader::ModuleName))
|
||||
),
|
||||
specialize_err(EHeader::Exposes, exposes_kw())
|
||||
),
|
||||
|arena: &'a bumpalo::Bump,
|
||||
(before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| {
|
||||
merge_n_spaces!(arena, before_name, kw.before, kw.after)
|
||||
}
|
||||
);
|
||||
|
||||
record!(ModuleHeader {
|
||||
before_exposes: before_exposes,
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"),
|
||||
interface_imports: map!(
|
||||
specialize_err(EHeader::Imports, imports()),
|
||||
imports_none_if_empty
|
||||
)
|
||||
.trace("imports"),
|
||||
})
|
||||
.trace("interface_header")
|
||||
}
|
||||
|
||||
fn imports_none_if_empty(value: ImportsKeywordItem<'_>) -> Option<ImportsKeywordItem<'_>> {
|
||||
if value.item.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
|
||||
record!(HostedHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(module_name_help(EHeader::ModuleName)),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_values()),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_values_kw()),
|
||||
imports: specialize_err(EHeader::Imports, imports()),
|
||||
generates: specialize_err(EHeader::Generates, generates()),
|
||||
generates_with: specialize_err(EHeader::GeneratesWith, generates_with()),
|
||||
|
@ -178,29 +240,194 @@ fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
|
|||
#[inline(always)]
|
||||
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||
record!(AppHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(crate::parser::specialize_err(
|
||||
EHeader::AppName,
|
||||
string_literal::parse_str_literal()
|
||||
)),
|
||||
packages: optional(specialize_err(EHeader::Packages, packages())),
|
||||
imports: optional(specialize_err(EHeader::Imports, imports())),
|
||||
provides: specialize_err(EHeader::Provides, provides_to()),
|
||||
before_provides: space0_e(EHeader::IndentStart),
|
||||
provides: specialize_err(EHeader::Exposes, exposes_list()),
|
||||
before_packages: space0_e(EHeader::IndentStart),
|
||||
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
|
||||
old_imports: succeed!(None),
|
||||
old_provides_to_new_package: succeed!(None),
|
||||
})
|
||||
.trace("app_header")
|
||||
}
|
||||
|
||||
struct OldAppHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub packages: Option<Loc<OldAppPackages<'a>>>,
|
||||
pub imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
|
||||
pub provides: ProvidesTo<'a>,
|
||||
}
|
||||
|
||||
type OldAppPackages<'a> =
|
||||
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>;
|
||||
|
||||
#[inline(always)]
|
||||
fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||
let old = record!(OldAppHeader {
|
||||
before_name: skip_second!(
|
||||
space0_e(EHeader::IndentStart),
|
||||
loc!(crate::parser::specialize_err(
|
||||
EHeader::AppName,
|
||||
string_literal::parse_str_literal()
|
||||
))
|
||||
),
|
||||
packages: optional(specialize_err(EHeader::Packages, loc!(packages()))),
|
||||
imports: optional(specialize_err(EHeader::Imports, imports())),
|
||||
provides: specialize_err(EHeader::Provides, provides_to()),
|
||||
});
|
||||
|
||||
map_with_arena!(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| {
|
||||
let mut before_packages: &'a [CommentOrNewline] = &[];
|
||||
|
||||
let packages = match old.packages {
|
||||
Some(packages) => {
|
||||
before_packages = merge_spaces(
|
||||
arena,
|
||||
packages.value.keyword.before,
|
||||
packages.value.keyword.after,
|
||||
);
|
||||
|
||||
if let To::ExistingPackage(platform_shorthand) = old.provides.to.value {
|
||||
packages.map(|coll| {
|
||||
coll.item.map_items(arena, |loc_spaced_pkg| {
|
||||
if loc_spaced_pkg.value.item().shorthand == platform_shorthand {
|
||||
loc_spaced_pkg.map(|spaced_pkg| {
|
||||
spaced_pkg.map(arena, |pkg| {
|
||||
let mut new_pkg = *pkg;
|
||||
new_pkg.platform_marker = Some(merge_spaces(
|
||||
arena,
|
||||
old.provides.to_keyword.before,
|
||||
old.provides.to_keyword.after,
|
||||
));
|
||||
new_pkg
|
||||
})
|
||||
})
|
||||
} else {
|
||||
*loc_spaced_pkg
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
packages.map(|kw| kw.item)
|
||||
}
|
||||
}
|
||||
None => Loc {
|
||||
region: Region::zero(),
|
||||
value: Collection::empty(),
|
||||
},
|
||||
};
|
||||
|
||||
let provides = match old.provides.types {
|
||||
Some(types) => {
|
||||
let mut combined_items = bumpalo::collections::Vec::with_capacity_in(
|
||||
old.provides.entries.items.len() + types.items.len(),
|
||||
arena,
|
||||
);
|
||||
|
||||
combined_items.extend_from_slice(old.provides.entries.items);
|
||||
|
||||
for loc_spaced_type_ident in types.items {
|
||||
combined_items.push(loc_spaced_type_ident.map(|spaced_type_ident| {
|
||||
spaced_type_ident.map(arena, |type_ident| {
|
||||
ExposedName::new(From::from(*type_ident))
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
let value_comments = old.provides.entries.final_comments();
|
||||
let type_comments = types.final_comments();
|
||||
|
||||
let mut combined_comments = bumpalo::collections::Vec::with_capacity_in(
|
||||
value_comments.len() + type_comments.len(),
|
||||
arena,
|
||||
);
|
||||
combined_comments.extend_from_slice(value_comments);
|
||||
combined_comments.extend_from_slice(type_comments);
|
||||
|
||||
Collection::with_items_and_comments(
|
||||
arena,
|
||||
combined_items.into_bump_slice(),
|
||||
combined_comments.into_bump_slice(),
|
||||
)
|
||||
}
|
||||
None => old.provides.entries,
|
||||
};
|
||||
|
||||
AppHeader {
|
||||
before_provides: merge_spaces(
|
||||
arena,
|
||||
old.before_name,
|
||||
old.provides.provides_keyword.before,
|
||||
),
|
||||
provides,
|
||||
before_packages: merge_spaces(
|
||||
arena,
|
||||
before_packages,
|
||||
old.provides.provides_keyword.after,
|
||||
),
|
||||
packages,
|
||||
old_imports: old.imports.and_then(imports_none_if_empty),
|
||||
old_provides_to_new_package: match old.provides.to.value {
|
||||
To::NewPackage(new_pkg) => Some(new_pkg),
|
||||
To::ExistingPackage(_) => None,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
|
||||
record!(PackageHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(specialize_err(EHeader::PackageName, package_name())),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
|
||||
packages: specialize_err(EHeader::Packages, packages()),
|
||||
before_exposes: space0_e(EHeader::IndentStart),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_module_collection()),
|
||||
before_packages: space0_e(EHeader::IndentStart),
|
||||
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
|
||||
})
|
||||
.trace("package_header")
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct OldPackageHeader<'a> {
|
||||
before_name: &'a [CommentOrNewline<'a>],
|
||||
exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||
packages:
|
||||
Loc<KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>>,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
|
||||
map_with_arena!(
|
||||
record!(OldPackageHeader {
|
||||
before_name: skip_second!(
|
||||
space0_e(EHeader::IndentStart),
|
||||
specialize_err(EHeader::PackageName, package_name())
|
||||
),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
|
||||
packages: specialize_err(EHeader::Packages, loc!(packages())),
|
||||
}),
|
||||
|arena: &'a bumpalo::Bump, old: OldPackageHeader<'a>| {
|
||||
let before_exposes = merge_n_spaces!(
|
||||
arena,
|
||||
old.before_name,
|
||||
old.exposes.keyword.before,
|
||||
old.exposes.keyword.after
|
||||
);
|
||||
let before_packages = merge_spaces(
|
||||
arena,
|
||||
old.packages.value.keyword.before,
|
||||
old.packages.value.keyword.after,
|
||||
);
|
||||
|
||||
PackageHeader {
|
||||
before_exposes,
|
||||
exposes: old.exposes.item,
|
||||
before_packages,
|
||||
packages: old.packages.map(|kw| kw.item),
|
||||
}
|
||||
}
|
||||
)
|
||||
.trace("old_package_header")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||
record!(PlatformHeader {
|
||||
|
@ -381,29 +608,40 @@ fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn exposes_values<'a>() -> impl Parser<
|
||||
fn exposes_values_kw<'a>() -> impl Parser<
|
||||
'a,
|
||||
KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
EExposes,
|
||||
> {
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
ExposesKeyword,
|
||||
EExposes::Exposes,
|
||||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
byte(b'[', EExposes::ListStart),
|
||||
exposes_entry(EExposes::Identifier),
|
||||
byte(b',', EExposes::ListEnd),
|
||||
byte(b']', EExposes::ListEnd),
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
keyword: exposes_kw(),
|
||||
item: exposes_list()
|
||||
})
|
||||
}
|
||||
|
||||
fn spaces_around_keyword<'a, K: Keyword, E>(
|
||||
#[inline(always)]
|
||||
fn exposes_kw<'a>() -> impl Parser<'a, Spaces<'a, ExposesKeyword>, EExposes> {
|
||||
spaces_around_keyword(
|
||||
ExposesKeyword,
|
||||
EExposes::Exposes,
|
||||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn exposes_list<'a>() -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>, EExposes>
|
||||
{
|
||||
collection_trailing_sep_e!(
|
||||
byte(b'[', EExposes::ListStart),
|
||||
exposes_entry(EExposes::Identifier),
|
||||
byte(b',', EExposes::ListEnd),
|
||||
byte(b']', EExposes::ListEnd),
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
pub fn spaces_around_keyword<'a, K: Keyword, E>(
|
||||
keyword_item: K,
|
||||
expectation: fn(Position) -> E,
|
||||
indent_problem1: fn(Position) -> E,
|
||||
|
@ -443,16 +681,21 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
byte(b'[', EExposes::ListStart),
|
||||
exposes_module(EExposes::Identifier),
|
||||
byte(b',', EExposes::ListEnd),
|
||||
byte(b']', EExposes::ListEnd),
|
||||
Spaced::SpaceBefore
|
||||
),
|
||||
item: exposes_module_collection(),
|
||||
})
|
||||
}
|
||||
|
||||
fn exposes_module_collection<'a>(
|
||||
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>, EExposes> {
|
||||
collection_trailing_sep_e!(
|
||||
byte(b'[', EExposes::ListStart),
|
||||
exposes_module(EExposes::Identifier),
|
||||
byte(b',', EExposes::ListEnd),
|
||||
byte(b']', EExposes::ListEnd),
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
fn exposes_module<'a, F, E>(
|
||||
to_expectation: F,
|
||||
) -> impl Parser<'a, Loc<Spaced<'a, ModuleName<'a>>>, E>
|
||||
|
@ -474,22 +717,33 @@ fn packages<'a>() -> impl Parser<
|
|||
EPackages<'a>,
|
||||
> {
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
PackagesKeyword,
|
||||
EPackages::Packages,
|
||||
EPackages::IndentPackages,
|
||||
EPackages::IndentListStart
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
byte(b'{', EPackages::ListStart),
|
||||
specialize_err(EPackages::PackageEntry, loc!(package_entry())),
|
||||
byte(b',', EPackages::ListEnd),
|
||||
byte(b'}', EPackages::ListEnd),
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
keyword: packages_kw(),
|
||||
item: packages_collection()
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'a>> {
|
||||
spaces_around_keyword(
|
||||
PackagesKeyword,
|
||||
EPackages::Packages,
|
||||
EPackages::IndentPackages,
|
||||
EPackages::IndentListStart,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn packages_collection<'a>(
|
||||
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>, EPackages<'a>> {
|
||||
collection_trailing_sep_e!(
|
||||
byte(b'{', EPackages::ListStart),
|
||||
specialize_err(EPackages::PackageEntry, loc!(package_entry())),
|
||||
byte(b',', EPackages::ListEnd),
|
||||
byte(b'}', EPackages::ListEnd),
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn generates<'a>(
|
||||
) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> {
|
||||
|
@ -552,7 +806,7 @@ fn imports<'a>() -> impl Parser<
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
|
||||
pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
|
||||
// e.g.
|
||||
//
|
||||
// printLine : Str -> Effect {}
|
||||
|
@ -590,7 +844,7 @@ fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> {
|
|||
specialize_err(|_, pos| EImports::Shorthand(pos), lowercase_ident())
|
||||
}
|
||||
|
||||
fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E>
|
||||
pub fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E>
|
||||
where
|
||||
F: Fn(Position) -> E,
|
||||
E: 'a,
|
||||
|
|
|
@ -87,6 +87,7 @@ impl_space_problem! {
|
|||
EGeneratesWith,
|
||||
EHeader<'a>,
|
||||
EIf<'a>,
|
||||
EImport<'a>,
|
||||
EImports,
|
||||
EInParens<'a>,
|
||||
EClosure<'a>,
|
||||
|
@ -210,6 +211,8 @@ pub enum EPackageEntry<'a> {
|
|||
Shorthand(Position),
|
||||
Colon(Position),
|
||||
IndentPackage(Position),
|
||||
IndentPlatform(Position),
|
||||
Platform(Position),
|
||||
Space(BadInputError, Position),
|
||||
}
|
||||
|
||||
|
@ -346,6 +349,7 @@ pub enum EExpr<'a> {
|
|||
|
||||
Expect(EExpect<'a>, Position),
|
||||
Dbg(EExpect<'a>, Position),
|
||||
Import(EImport<'a>, Position),
|
||||
|
||||
Closure(EClosure<'a>, Position),
|
||||
Underscore(Position),
|
||||
|
@ -519,6 +523,29 @@ pub enum EExpect<'a> {
|
|||
IndentCondition(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EImport<'a> {
|
||||
Import(Position),
|
||||
IndentStart(Position),
|
||||
PackageShorthand(Position),
|
||||
PackageShorthandDot(Position),
|
||||
ModuleName(Position),
|
||||
IndentAs(Position),
|
||||
As(Position),
|
||||
IndentAlias(Position),
|
||||
Alias(Position),
|
||||
IndentExposing(Position),
|
||||
Exposing(Position),
|
||||
ExposingListStart(Position),
|
||||
ExposedName(Position),
|
||||
ExposingListEnd(Position),
|
||||
IndentIngestedPath(Position),
|
||||
IngestedPath(Position),
|
||||
IndentIngestedName(Position),
|
||||
IngestedName(ETypedIdent<'a>, Position),
|
||||
Space(BadInputError, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EPattern<'a> {
|
||||
Record(PRecord<'a>, Position),
|
||||
|
@ -1685,7 +1712,7 @@ macro_rules! record {
|
|||
/// Similar to [`and!`], but we modify the `min_indent` of the second parser to be
|
||||
/// 1 greater than the `line_indent()` at the start of the first parser.
|
||||
#[macro_export]
|
||||
macro_rules! indented_seq {
|
||||
macro_rules! indented_seq_skip_first {
|
||||
($p1:expr, $p2:expr) => {
|
||||
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
|
||||
let start_indent = state.line_indent();
|
||||
|
@ -1710,9 +1737,36 @@ macro_rules! indented_seq {
|
|||
};
|
||||
}
|
||||
|
||||
/// Similar to [`and!`], but we modify the min_indent of the second parser to be
|
||||
/// 1 greater than the line_indent() at the start of the first parser.
|
||||
#[macro_export]
|
||||
macro_rules! indented_seq {
|
||||
($p1:expr, $p2:expr) => {
|
||||
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
|
||||
let start_indent = state.line_indent();
|
||||
|
||||
// TODO: we should account for min_indent here, but this doesn't currently work
|
||||
// because min_indent is sometimes larger than it really should be, which is in turn
|
||||
// due to uses of `increment_indent`.
|
||||
//
|
||||
// let p1_indent = std::cmp::max(start_indent, min_indent);
|
||||
|
||||
let p1_indent = start_indent;
|
||||
let p2_indent = p1_indent + 1;
|
||||
|
||||
match $p1.parse(arena, state, p1_indent) {
|
||||
Ok((p1, out1, state)) => match $p2.parse(arena, state, p2_indent) {
|
||||
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
|
||||
Err((p2, fail)) => Err((p1.or(p2), fail)),
|
||||
},
|
||||
Err((progress, fail)) => Err((progress, fail)),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Similar to [`and!`], but we modify the `min_indent` of the second parser to be
|
||||
/// 1 greater than the `column()` at the start of the first parser.
|
||||
#[macro_export]
|
||||
macro_rules! absolute_indented_seq {
|
||||
($p1:expr, $p2:expr) => {
|
||||
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::ast;
|
||||
use crate::ast::Defs;
|
||||
use crate::module::module_defs;
|
||||
use crate::parser::Parser;
|
||||
use crate::module::parse_module_defs;
|
||||
use crate::parser::SourceError;
|
||||
use crate::parser::SyntaxError;
|
||||
use crate::state::State;
|
||||
|
@ -34,12 +33,7 @@ pub fn parse_loc_with<'a>(
|
|||
pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
|
||||
let min_indent = 0;
|
||||
|
||||
match module_defs().parse(arena, state, min_indent) {
|
||||
Ok(tuple) => Ok(tuple.1),
|
||||
Err(tuple) => Err(tuple.1),
|
||||
}
|
||||
parse_module_defs(arena, state, Defs::default())
|
||||
}
|
||||
|
||||
pub fn parse_header_with<'a>(
|
||||
|
|
|
@ -392,7 +392,7 @@ fn record_type<'a>(
|
|||
|
||||
fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
indented_seq!(
|
||||
specialize_err(EType::TApply, concrete_type()),
|
||||
// Optionally parse space-separated arguments for the constructor,
|
||||
// e.g. `Str Float` in `Map Str Float`
|
||||
|
|
|
@ -22,8 +22,8 @@ mod test_parse {
|
|||
use roc_parse::ast::StrSegment::*;
|
||||
use roc_parse::ast::{self, EscapedChar};
|
||||
use roc_parse::ast::{CommentOrNewline, StrLiteral::*};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{Parser, SyntaxError};
|
||||
use roc_parse::module::parse_module_defs;
|
||||
use roc_parse::parser::SyntaxError;
|
||||
use roc_parse::state::State;
|
||||
use roc_parse::test_helpers::parse_expr_with;
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
@ -345,9 +345,7 @@ mod test_parse {
|
|||
List.map list isTest
|
||||
"
|
||||
);
|
||||
let actual = module_defs()
|
||||
.parse(&arena, State::new(src.as_bytes()), 0)
|
||||
.map(|tuple| tuple.1);
|
||||
let actual = parse_module_defs(&arena, State::new(src.as_bytes()), ast::Defs::default());
|
||||
|
||||
// It should occur twice in the debug output - once for the pattern,
|
||||
// and then again for the lookup.
|
||||
|
@ -371,13 +369,12 @@ mod test_parse {
|
|||
);
|
||||
|
||||
let state = State::new(src.as_bytes());
|
||||
let parser = module_defs();
|
||||
let parsed = parser.parse(arena, state, 0);
|
||||
let parsed = parse_module_defs(arena, state, ast::Defs::default());
|
||||
match parsed {
|
||||
Ok((_, _, _state)) => {
|
||||
Ok(_) => {
|
||||
// dbg!(_state);
|
||||
}
|
||||
Err((_, _fail)) => {
|
||||
Err(_) => {
|
||||
// dbg!(_fail, _state);
|
||||
panic!("Failed to parse!");
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::path::PathBuf;
|
|||
use roc_collections::all::MutSet;
|
||||
use roc_module::called_via::BinOp;
|
||||
use roc_module::ident::{Ident, Lowercase, ModuleName, TagName};
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_module::symbol::{ModuleId, ScopeModuleSource, Symbol};
|
||||
use roc_parse::ast::Base;
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
@ -40,6 +40,20 @@ pub enum Problem {
|
|||
UnusedModuleImport(ModuleId, Region),
|
||||
ExposedButNotDefined(Symbol),
|
||||
UnknownGeneratesWith(Loc<Ident>),
|
||||
ImportNameConflict {
|
||||
name: ModuleName,
|
||||
is_alias: bool,
|
||||
new_module_id: ModuleId,
|
||||
new_import_region: Region,
|
||||
existing_import: ScopeModuleSource,
|
||||
},
|
||||
ExplicitBuiltinImport(ModuleId, Region),
|
||||
ExplicitBuiltinTypeImport(Symbol, Region),
|
||||
ImportShadowsSymbol {
|
||||
region: Region,
|
||||
new_symbol: Symbol,
|
||||
existing_symbol_region: Region,
|
||||
},
|
||||
/// First symbol is the name of the closure with that argument
|
||||
/// Bool is whether the closure is anonymous
|
||||
/// Second symbol is the name of the argument that is unused
|
||||
|
@ -221,6 +235,10 @@ impl Problem {
|
|||
Problem::UnusedDef(_, _) => Warning,
|
||||
Problem::UnusedImport(_, _) => Warning,
|
||||
Problem::UnusedModuleImport(_, _) => Warning,
|
||||
Problem::ImportNameConflict { .. } => RuntimeError,
|
||||
Problem::ExplicitBuiltinImport(_, _) => Warning,
|
||||
Problem::ExplicitBuiltinTypeImport(_, _) => Warning,
|
||||
Problem::ImportShadowsSymbol { .. } => RuntimeError,
|
||||
Problem::ExposedButNotDefined(_) => RuntimeError,
|
||||
Problem::UnknownGeneratesWith(_) => RuntimeError,
|
||||
Problem::UnusedArgument(_, _, _, _) => Warning,
|
||||
|
@ -295,6 +313,13 @@ impl Problem {
|
|||
}
|
||||
| Problem::UnusedImport(_, region)
|
||||
| Problem::UnusedModuleImport(_, region)
|
||||
| Problem::ImportNameConflict {
|
||||
new_import_region: region,
|
||||
..
|
||||
}
|
||||
| Problem::ExplicitBuiltinImport(_, region)
|
||||
| Problem::ExplicitBuiltinTypeImport(_, region)
|
||||
| Problem::ImportShadowsSymbol { region, .. }
|
||||
| Problem::UnknownGeneratesWith(Loc { region, .. })
|
||||
| Problem::UnusedArgument(_, _, _, region)
|
||||
| Problem::UnusedBranchDef(_, region)
|
||||
|
@ -368,6 +393,7 @@ impl Problem {
|
|||
| Problem::RuntimeError(RuntimeError::DegenerateBranch(region))
|
||||
| Problem::RuntimeError(RuntimeError::MultipleRecordBuilders(region))
|
||||
| Problem::RuntimeError(RuntimeError::UnappliedRecordBuilder(region))
|
||||
| Problem::RuntimeError(RuntimeError::ReadIngestedFileError { region, .. })
|
||||
| Problem::InvalidAliasRigid { region, .. }
|
||||
| Problem::InvalidInterpolation(region)
|
||||
| Problem::InvalidHexadecimal(region)
|
||||
|
@ -576,6 +602,11 @@ pub enum RuntimeError {
|
|||
/// If unsure, this should be set to `false`
|
||||
module_exists: bool,
|
||||
},
|
||||
ReadIngestedFileError {
|
||||
filename: PathBuf,
|
||||
error: io::ErrorKind,
|
||||
region: Region,
|
||||
},
|
||||
InvalidPrecedence(PrecedenceProblem, Region),
|
||||
MalformedIdentifier(Box<str>, roc_parse::ident::BadIdent, Region),
|
||||
MalformedTypeName(Box<str>, Region),
|
||||
|
@ -659,7 +690,8 @@ impl RuntimeError {
|
|||
| RuntimeError::InvalidHexadecimal(region)
|
||||
| RuntimeError::MultipleRecordBuilders(region)
|
||||
| RuntimeError::UnappliedRecordBuilder(region)
|
||||
| RuntimeError::InvalidUnicodeCodePt(region) => *region,
|
||||
| RuntimeError::ReadIngestedFileError { region, .. } => *region,
|
||||
RuntimeError::InvalidUnicodeCodePt(region) => *region,
|
||||
RuntimeError::UnresolvedTypeVar | RuntimeError::ErroneousType => Region::zero(),
|
||||
RuntimeError::LookupNotInScope { loc_name, .. } => loc_name.region,
|
||||
RuntimeError::OpaqueNotDefined { usage, .. } => usage.region,
|
||||
|
|
|
@ -958,7 +958,7 @@ fn sort_and_deduplicate<T>(tag_vars: &mut bumpalo::collections::Vec<(TagName, T)
|
|||
fn find_tag_name_run(slice: &[TagName], subs: &mut Subs) -> Option<SubsSlice<TagName>> {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
let tag_name = slice.get(0)?;
|
||||
let tag_name = slice.first()?;
|
||||
|
||||
let mut result = None;
|
||||
|
||||
|
|
|
@ -3448,7 +3448,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Result.{ Result }] provides [main] to "./platform"
|
||||
app "test" imports [] provides [main] to "./platform"
|
||||
|
||||
boom = \_ -> boom {}
|
||||
|
||||
|
|
|
@ -355,7 +355,7 @@ fn encode_use_stdlib() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := {} implements [Encoding {toEncoder}]
|
||||
|
@ -383,7 +383,7 @@ fn encode_use_stdlib_without_wrapping_custom() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := {} implements [Encoding {toEncoder}]
|
||||
|
@ -433,7 +433,7 @@ fn to_encoder_encode_custom_has_capture() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := Str implements [Encoding {toEncoder}]
|
||||
|
@ -473,7 +473,7 @@ mod encode_immediate {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, TotallyNotJson] provides [main] to "./platform"
|
||||
app "test" imports [TotallyNotJson] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.fromUtf8 (Encode.toBytes "foo" TotallyNotJson.json) is
|
||||
|
@ -492,7 +492,7 @@ mod encode_immediate {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, TotallyNotJson] provides [main] to "./platform"
|
||||
app "test" imports [TotallyNotJson] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.fromUtf8 (Encode.toBytes [1, 2, 3] TotallyNotJson.json) is
|
||||
|
@ -511,7 +511,7 @@ mod encode_immediate {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, TotallyNotJson] provides [main] to "./platform"
|
||||
app "test" imports [TotallyNotJson] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.fromUtf8 (Encode.toBytes Bool.false TotallyNotJson.json) is
|
||||
|
@ -532,7 +532,7 @@ mod encode_immediate {
|
|||
assert_evals_to!(
|
||||
&format!(indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, TotallyNotJson] provides [main] to "./platform"
|
||||
app "test" imports [TotallyNotJson] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.fromUtf8 (Encode.toBytes {}{} TotallyNotJson.json) is
|
||||
|
@ -572,7 +572,7 @@ fn encode_derived_record_one_field_string() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -595,7 +595,7 @@ fn encode_derived_record_two_fields_strings() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -619,7 +619,7 @@ fn encode_derived_nested_record_string() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -643,7 +643,7 @@ fn encode_derived_tag_one_payload_string() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -666,7 +666,7 @@ fn encode_derived_tag_two_payloads_string() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -689,7 +689,7 @@ fn encode_derived_nested_tag_string() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -714,7 +714,7 @@ fn encode_derived_nested_record_tag_record() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -738,7 +738,7 @@ fn encode_derived_list_string() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -763,7 +763,7 @@ fn encode_derived_list_of_records() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -787,7 +787,7 @@ fn encode_derived_list_of_lists_of_strings() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -812,7 +812,7 @@ fn encode_derived_record_with_many_types() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -839,7 +839,7 @@ fn encode_derived_tuple_two_fields() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -862,7 +862,7 @@ fn encode_derived_tuple_of_tuples() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -886,7 +886,7 @@ fn encode_derived_generic_record_with_different_field_types() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
Q a b := {a: a, b: b} implements [Encoding]
|
||||
|
@ -912,7 +912,7 @@ fn encode_derived_generic_tag_with_different_field_types() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
Q a b := [A a, B b] implements [Encoding]
|
||||
|
@ -940,7 +940,7 @@ fn specialize_unique_newtype_records() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Encode, TotallyNotJson]
|
||||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
|
@ -1056,7 +1056,7 @@ mod decode_immediate {
|
|||
use indoc::indoc;
|
||||
|
||||
#[cfg(all(test, feature = "gen-llvm"))]
|
||||
use roc_std::RocStr;
|
||||
use roc_std::{RocStr, I128, U128};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
|
@ -1121,7 +1121,7 @@ mod decode_immediate {
|
|||
}
|
||||
|
||||
macro_rules! num_immediate {
|
||||
($($num:expr, $typ:ident)*) => {$(
|
||||
($($num:expr, $typ:ident, $expected_type:ident)*) => {$(
|
||||
#[test]
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
fn $typ() {
|
||||
|
@ -1137,25 +1137,25 @@ mod decode_immediate {
|
|||
"#
|
||||
), $num, stringify!($typ), stringify!($typ)),
|
||||
$num,
|
||||
$typ
|
||||
$expected_type
|
||||
)
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
num_immediate! {
|
||||
17, i8
|
||||
17, i16
|
||||
17, i32
|
||||
17, i64
|
||||
17, i128
|
||||
17, u8
|
||||
17, u16
|
||||
17, u32
|
||||
17, u64
|
||||
17, u128
|
||||
17.23, f32
|
||||
17.23, f64
|
||||
17, i8, i8
|
||||
17, i16, i16
|
||||
17, i32, i32
|
||||
17, i64, i64
|
||||
I128::from(17), i128, I128
|
||||
17, u8, u8
|
||||
17, u16, u16
|
||||
17, u32, u32
|
||||
17, u64, u64
|
||||
U128::from(17), u128, U128
|
||||
17.23, f32, f32
|
||||
17.23, f64, f64
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::helpers::wasm::assert_evals_to;
|
|||
#[allow(unused_imports)]
|
||||
use indoc::indoc;
|
||||
#[allow(unused_imports)]
|
||||
use roc_std::{RocDec, RocOrder, RocResult};
|
||||
use roc_std::{RocDec, RocOrder, RocResult, I128, U128};
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
|
@ -42,8 +42,8 @@ fn i128_signed_int_alias() {
|
|||
i
|
||||
"
|
||||
),
|
||||
128,
|
||||
i128
|
||||
I128::from(128),
|
||||
I128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -126,8 +126,8 @@ fn i128_hex_int_alias() {
|
|||
f
|
||||
"
|
||||
),
|
||||
0x123,
|
||||
i128
|
||||
I128::from(0x123),
|
||||
I128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -207,8 +207,8 @@ fn u128_signed_int_alias() {
|
|||
i
|
||||
"
|
||||
),
|
||||
128,
|
||||
u128
|
||||
U128::from(128),
|
||||
U128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -288,8 +288,8 @@ fn u128_hex_int_alias() {
|
|||
f
|
||||
"
|
||||
),
|
||||
0x123,
|
||||
i128
|
||||
I128::from(0x123),
|
||||
I128
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -429,8 +429,8 @@ fn dec_float_alias() {
|
|||
x
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("2.1"),
|
||||
i128
|
||||
RocDec::from_str("2.1").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -568,14 +568,14 @@ fn various_sized_abs() {
|
|||
assert_evals_to!("Num.abs -6i32", 6, i32);
|
||||
assert_evals_to!("Num.abs -6i64", 6, i64);
|
||||
if !cfg!(feature = "gen-wasm") {
|
||||
assert_evals_to!("Num.abs -6i128", 6, i128);
|
||||
assert_evals_to!("Num.abs -6i128", I128::from(6), I128);
|
||||
}
|
||||
assert_evals_to!("Num.abs 6u8", 6, u8);
|
||||
assert_evals_to!("Num.abs 6u16", 6, u16);
|
||||
assert_evals_to!("Num.abs 6u32", 6, u32);
|
||||
assert_evals_to!("Num.abs 6u64", 6, u64);
|
||||
if !cfg!(feature = "gen-wasm") {
|
||||
assert_evals_to!("Num.abs 6u128", 6, u128);
|
||||
assert_evals_to!("Num.abs 6u128", U128::from(6), U128);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,8 +652,8 @@ fn gen_add_dec() {
|
|||
z
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("5.2"),
|
||||
i128
|
||||
RocDec::from_str("5.2").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -742,8 +742,8 @@ fn gen_div_dec() {
|
|||
x / y
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("3.333333333333333333"),
|
||||
i128
|
||||
RocDec::from_str("3.333333333333333333").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -764,8 +764,8 @@ fn gen_div_checked_dec() {
|
|||
Err _ -> -1
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("3.333333333333333333"),
|
||||
i128
|
||||
RocDec::from_str("3.333333333333333333").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -785,8 +785,8 @@ fn gen_div_checked_by_zero_dec() {
|
|||
Err _ -> -1
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("-1"),
|
||||
i128
|
||||
RocDec::from_str("-1").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -794,7 +794,7 @@ fn gen_div_checked_by_zero_dec() {
|
|||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
#[should_panic(expected = r#"Roc failed with message: "Decimal division by 0!"#)]
|
||||
fn gen_div_dec_by_zero() {
|
||||
assert_evals_to!("1dec / 0", RocDec::from_str_to_i128_unsafe("-1"), i128);
|
||||
assert_evals_to!("1dec / 0", RocDec::from_str("-1").unwrap(), RocDec);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1030,8 +1030,8 @@ fn gen_sub_dec() {
|
|||
(x - y) - z
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("-3.9"),
|
||||
i128
|
||||
RocDec::from_str("-3.9").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1053,8 +1053,8 @@ fn gen_mul_dec() {
|
|||
x * y * z
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("48.0"),
|
||||
i128
|
||||
RocDec::from_str("48.0").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2064,7 +2064,7 @@ fn int_mul_wrap_i64() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn int_mul_wrap_i128() {
|
||||
assert_evals_to!("Num.mulWrap Num.maxI128 2", -2, i128);
|
||||
assert_evals_to!("Num.mulWrap Num.maxI128 2", I128::from(-2), I128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2195,13 +2195,13 @@ fn shift_right_cast_i8() {
|
|||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn min_i128() {
|
||||
assert_evals_to!("Num.minI128", i128::MIN, i128);
|
||||
assert_evals_to!("Num.minI128", I128::from(i128::MIN), I128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn max_i128() {
|
||||
assert_evals_to!("Num.maxI128", i128::MAX, i128);
|
||||
assert_evals_to!("Num.maxI128", I128::from(i128::MAX), I128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2498,13 +2498,6 @@ to_int_checked_tests! {
|
|||
to_i64_checked_larger_width_unsigned_fits_pos, "15u128", 15
|
||||
to_i64_checked_larger_width_unsigned_oob_pos, "9223372036854775808u128", None
|
||||
)
|
||||
"Num.toI128Checked", i128, (
|
||||
to_i128_checked_smaller_width_pos, "15i8", 15
|
||||
to_i128_checked_smaller_width_neg, "-15i8", -15
|
||||
to_i128_checked_same, "15i128", 15
|
||||
to_i128_checked_same_width_unsigned_fits, "15u128", 15
|
||||
to_i128_checked_same_width_unsigned_oob, "170141183460469231731687303715884105728u128", None
|
||||
)
|
||||
"Num.toU8Checked", u8, (
|
||||
to_u8_checked_same, "15u8", 15
|
||||
to_u8_checked_same_width_signed_fits, "15i8", 15
|
||||
|
@ -2551,13 +2544,81 @@ to_int_checked_tests! {
|
|||
to_u64_checked_larger_width_unsigned_fits_pos, "15u128", 15
|
||||
to_u64_checked_larger_width_unsigned_oob_pos, "18446744073709551616u128", None
|
||||
)
|
||||
"Num.toU128Checked", u128, (
|
||||
to_u128_checked_smaller_width_pos, "15i8", 15
|
||||
to_u128_checked_smaller_width_neg_oob, "-15i8", None
|
||||
to_u128_checked_same, "15u128", 15
|
||||
to_u128_checked_same_width_signed_fits, "15i128", 15
|
||||
to_u128_checked_same_width_signed_oob, "-1i128", None
|
||||
)
|
||||
}
|
||||
|
||||
fn wrap_with_default(test_roc_code: &str) -> String {
|
||||
format!("Result.withDefault ({}) 123454321", test_roc_code)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_i128_checked_smaller_width_pos() {
|
||||
let test_roc_code = wrap_with_default("Num.toI128Checked 15i8");
|
||||
assert_evals_to!(&test_roc_code, I128::from(15), I128)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_i128_checked_smaller_width_neg() {
|
||||
let test_roc_code = wrap_with_default("Num.toI128Checked -15i8");
|
||||
assert_evals_to!(&test_roc_code, I128::from(-15), I128)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_i128_checked_same() {
|
||||
let test_roc_code = wrap_with_default("Num.toI128Checked 15i128");
|
||||
assert_evals_to!(&test_roc_code, I128::from(15), I128)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_i128_checked_same_width_unsigned_fits() {
|
||||
let test_roc_code = wrap_with_default("Num.toI128Checked 15u128");
|
||||
assert_evals_to!(&test_roc_code, I128::from(15), I128)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_i128_checked_same_width_unsigned_oob() {
|
||||
let test_roc_code =
|
||||
"Result.isErr (Num.toI128Checked 170141183460469231731687303715884105728u128)";
|
||||
assert_evals_to!(&test_roc_code, true, bool)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_u128_checked_smaller_width_pos() {
|
||||
let test_roc_code = wrap_with_default("Num.toU128Checked 15i8");
|
||||
assert_evals_to!(&test_roc_code, U128::from(15), U128)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_u128_checked_smaller_width_neg_oob() {
|
||||
let test_roc_code = "Result.isErr (Num.toU128Checked -15i8)";
|
||||
assert_evals_to!(&test_roc_code, true, bool)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_u128_checked_same() {
|
||||
let test_roc_code = wrap_with_default("Num.toU128Checked 15u128");
|
||||
assert_evals_to!(&test_roc_code, U128::from(15), U128)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_u128_checked_same_width_signed_fits() {
|
||||
let test_roc_code = wrap_with_default("Num.toU128Checked 15i128");
|
||||
assert_evals_to!(&test_roc_code, U128::from(15), U128)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn to_u128_checked_same_width_signed_oob() {
|
||||
let test_roc_code = "Result.isErr (Num.toU128Checked -1i128)";
|
||||
assert_evals_to!(&test_roc_code, true, bool)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3263,8 +3324,8 @@ fn dec_float_suffix() {
|
|||
123.0dec
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("123.0"),
|
||||
i128
|
||||
RocDec::from_str("123.0").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3277,8 +3338,8 @@ fn dec_no_decimal() {
|
|||
3dec
|
||||
"
|
||||
),
|
||||
RocDec::from_str_to_i128_unsafe("3.0"),
|
||||
i128
|
||||
RocDec::from_str("3.0").unwrap(),
|
||||
RocDec
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3356,8 +3417,11 @@ fn promote_i128_number_layout() {
|
|||
}
|
||||
"
|
||||
),
|
||||
(18446744073709551617, -9223372036854775808),
|
||||
(i128, i128)
|
||||
(
|
||||
I128::from(18446744073709551617),
|
||||
I128::from(-9223372036854775808)
|
||||
),
|
||||
(I128, I128)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3370,8 +3434,8 @@ fn promote_u128_number_layout() {
|
|||
170141183460469231731687303715884105728 + 1
|
||||
"
|
||||
),
|
||||
170141183460469231731687303715884105729,
|
||||
u128
|
||||
U128::from(170141183460469231731687303715884105729),
|
||||
U128
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3498,10 +3562,14 @@ fn num_abs_diff_int() {
|
|||
#[test]
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
fn num_abs_diff_large_bits() {
|
||||
assert_evals_to!(r"Num.absDiff 0u128 0u128", 0, u128);
|
||||
assert_evals_to!(r"Num.absDiff 1u128 2u128", 1, u128);
|
||||
assert_evals_to!(r"Num.absDiff -1i128 1i128", 2, i128);
|
||||
assert_evals_to!(r"Num.absDiff Num.minI128 -1i128", i128::MAX, i128);
|
||||
assert_evals_to!(r"Num.absDiff 0u128 0u128", U128::from(0), U128);
|
||||
assert_evals_to!(r"Num.absDiff 1u128 2u128", U128::from(1), U128);
|
||||
assert_evals_to!(r"Num.absDiff -1i128 1i128", I128::from(2), I128);
|
||||
assert_evals_to!(
|
||||
r"Num.absDiff Num.minI128 -1i128",
|
||||
I128::from(i128::MAX),
|
||||
I128
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3532,7 +3600,7 @@ fn num_abs_int_min_overflow() {
|
|||
#[cfg(feature = "gen-llvm")]
|
||||
#[should_panic(expected = r#"Roc failed with message: "Integer subtraction overflowed!"#)]
|
||||
fn num_abs_large_bits_min_overflow() {
|
||||
assert_evals_to!(r"Num.absDiff Num.minI128 0", 0, i128);
|
||||
assert_evals_to!(r"Num.absDiff Num.minI128 0", I128::from(0), I128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3619,8 +3687,8 @@ fn mul_checked_u128() {
|
|||
x
|
||||
"
|
||||
),
|
||||
RocResult::ok(5u128 * 2u128),
|
||||
RocResult<u128, ()>
|
||||
RocResult::ok(U128::from(5u128 * 2u128)),
|
||||
RocResult<U128, ()>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3636,8 +3704,8 @@ fn sub_checked_u128() {
|
|||
x
|
||||
"
|
||||
),
|
||||
RocResult::ok(5u128 - 2u128),
|
||||
RocResult<u128, ()>
|
||||
RocResult::ok(U128::from(5u128 - 2u128)),
|
||||
RocResult<U128, ()>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3653,8 +3721,8 @@ fn add_checked_u128() {
|
|||
x
|
||||
"
|
||||
),
|
||||
RocResult::ok(5u128 + 2u128),
|
||||
RocResult<u128, ()>
|
||||
RocResult::ok(U128::from(5u128 + 2u128)),
|
||||
RocResult<U128, ()>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3717,18 +3785,18 @@ fn without_decimal_point() {
|
|||
);
|
||||
assert_evals_to!(
|
||||
r"Num.withoutDecimalPoint 123.000000000000000000",
|
||||
123000000000000000000,
|
||||
i128
|
||||
I128::from(123000000000000000000),
|
||||
I128
|
||||
);
|
||||
assert_evals_to!(
|
||||
r"Num.withoutDecimalPoint 170141183460469231731.687303715884105727",
|
||||
i128::MAX,
|
||||
i128
|
||||
I128::from(i128::MAX),
|
||||
I128
|
||||
);
|
||||
assert_evals_to!(
|
||||
r"Num.withoutDecimalPoint -170141183460469231731.687303715884105728",
|
||||
i128::MIN,
|
||||
i128
|
||||
I128::from(i128::MIN),
|
||||
I128
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::helpers::dev::assert_evals_to as assert_llvm_evals_to;
|
|||
#[allow(unused_imports)]
|
||||
use indoc::indoc;
|
||||
#[allow(unused_imports)]
|
||||
use roc_std::{RocList, RocResult, RocStr};
|
||||
use roc_std::{RocList, RocResult, RocStr, I128, U128};
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
|
@ -1345,8 +1345,8 @@ fn str_to_i128() {
|
|||
Str.toI128 "1"
|
||||
"#
|
||||
),
|
||||
RocResult::ok(1),
|
||||
RocResult<i128, ()>
|
||||
RocResult::ok(I128::from(1)),
|
||||
RocResult<I128, ()>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1359,11 +1359,12 @@ fn str_to_u128() {
|
|||
Str.toU128 "1"
|
||||
"#
|
||||
),
|
||||
RocResult::ok(1),
|
||||
RocResult<u128, ()>
|
||||
RocResult::ok(U128::from(1)),
|
||||
RocResult<U128, ()>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO add alignment check between i64 and I64 somewhere
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
fn str_to_i64() {
|
||||
|
|
|
@ -15,7 +15,7 @@ use indoc::indoc;
|
|||
|
||||
use roc_mono::layout::{LayoutRepr, STLayoutInterner};
|
||||
#[cfg(test)]
|
||||
use roc_std::{RocList, RocStr, U128};
|
||||
use roc_std::{RocList, RocStr, I128, U128};
|
||||
|
||||
#[test]
|
||||
fn width_and_alignment_u8_u8() {
|
||||
|
@ -1779,7 +1779,24 @@ fn alignment_i128() {
|
|||
x
|
||||
#"
|
||||
),
|
||||
// NOTE: roc_std::U128 is always aligned to 16, unlike rust's u128
|
||||
// NOTE: roc_std::I128 is always aligned to 16, unlike rust's i128
|
||||
((I128::from(42), true), 1),
|
||||
((I128, bool), u8)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn alignment_u128() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"#
|
||||
x : [One U128 Bool, Empty]
|
||||
x = One 42 (1 == 1)
|
||||
x
|
||||
#"
|
||||
),
|
||||
// NOTE: roc_std::U128 is always aligned to 16, unlike rust's i128
|
||||
((U128::from(42), true), 1),
|
||||
((U128, bool), u8)
|
||||
);
|
||||
|
|
|
@ -335,6 +335,9 @@ pub fn helper<'a>(
|
|||
let (main_fn_name, delayed_errors, module) =
|
||||
create_llvm_module(arena, src, config, context, target, function_kind);
|
||||
|
||||
// for debugging:
|
||||
//module.print_to_file(std::path::Path::new("/home/username/roc/llvm_ir.ll")).unwrap();
|
||||
|
||||
if !config.emit_debug_info {
|
||||
module.strip_debug_info();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::helpers::wasm::assert_evals_to;
|
|||
|
||||
#[allow(unused_imports)]
|
||||
use indoc::indoc;
|
||||
use roc_std::{RocList, RocStr};
|
||||
use roc_std::{RocList, RocStr, I128, U128};
|
||||
|
||||
#[test]
|
||||
fn str_split_empty_delimiter() {
|
||||
|
@ -998,8 +998,8 @@ fn str_to_i128() {
|
|||
Err _ -> 0
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i128
|
||||
I128::from(1),
|
||||
I128
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1013,8 +1013,8 @@ fn str_to_u128() {
|
|||
Err _ -> 0
|
||||
"#
|
||||
),
|
||||
1,
|
||||
u128
|
||||
U128::from(1),
|
||||
U128
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,4 +37,4 @@
|
|||
":="
|
||||
":"
|
||||
"@"
|
||||
"->"
|
||||
"->"
|
||||
|
|
|
@ -2,7 +2,7 @@ use bumpalo::Bump;
|
|||
use roc_fmt::{annotation::Formattable, module::fmt_module};
|
||||
use roc_parse::{
|
||||
ast::{Defs, Expr, Malformed, Module},
|
||||
module::module_defs,
|
||||
module::parse_module_defs,
|
||||
parser::{Parser, SyntaxError},
|
||||
state::State,
|
||||
test_helpers::{parse_defs_with, parse_expr_with, parse_header_with},
|
||||
|
@ -170,9 +170,9 @@ impl<'a> Input<'a> {
|
|||
.parse(arena, state.clone(), min_indent)
|
||||
.map_err(|(_, fail)| SyntaxError::Header(fail))?;
|
||||
|
||||
let (_, module_defs, _state) = module_defs()
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_, fail)| fail)?;
|
||||
let (header, defs) = header.upgrade_header_imports(arena);
|
||||
|
||||
let module_defs = parse_module_defs(arena, state, defs).unwrap();
|
||||
|
||||
Ok(Output::Full {
|
||||
header,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
NotEndOfFile(@12)
|
|
@ -0,0 +1 @@
|
|||
import Json as json
|
|
@ -1 +1 @@
|
|||
Header(Imports(ListEnd(@87), @65))
|
||||
Header(Exposes(ListEnd(@11), @4))
|
|
@ -1,4 +1 @@
|
|||
app "test-missing-comma"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task Base64]
|
||||
provides [main, @Foo] to pf
|
||||
app [main, @Foo] { pf: platform "platform/main.roc" }
|
||||
|
|
|
@ -1 +1 @@
|
|||
app "test-app" packages {} imports [] provides [] to blah
|
||||
app [] {}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue