diff --git a/crates/compiler/builtins/roc/Json.roc b/crates/compiler/builtins/roc/Json.roc index bd66d8f011..537437f53a 100644 --- a/crates/compiler/builtins/roc/Json.roc +++ b/crates/compiler/builtins/roc/Json.roc @@ -8,6 +8,7 @@ interface Json Str, Encode.{ Encoder, + EncoderFormatting, custom, appendWith, u8, @@ -31,7 +32,7 @@ interface Json }, ] -Json := {} +Json := {} has [EncoderFormatting {u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64, dec, bool, string, list, record, tag}] toUtf8 = @Json {} diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 5b9c04926d..ee48e4a6f6 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -445,13 +445,19 @@ fn canonicalize_claimed_ability_impl<'a>( name: label_str.to_owned(), region, }); + return Err(()); } }; match scope.lookup_ability_member_shadow(member_symbol) { - Some(symbol) => { - return Ok((member_symbol, symbol)); + Some(impl_symbol) => { + // TODO: get rid of register_specializing_symbol + scope + .abilities_store + .register_specializing_symbol(impl_symbol, member_symbol); + + Ok((member_symbol, impl_symbol)) } None => { env.problem(Problem::ImplementationNotFound { diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index 3cdb94595d..dd43015027 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -327,10 +327,6 @@ impl Scope { Err((loc_original_shadow.region, shadow, shadow_symbol)) } None => { - // TODO: remove register_specializing_symbol - self.abilities_store - .register_specializing_symbol(shadow_symbol, original_symbol); - self.shadows .insert(original_symbol, Loc::at(region, shadow_symbol)); diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 2f571d2507..ef1af9a8cd 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -5940,7 +5940,7 @@ mod solve_expr { Hash has hash : a -> U64 | a has Hash - Id := U64 + Id := U64 has [Hash {hash}] hash = \@Id n -> n "# @@ -5960,7 +5960,7 @@ mod solve_expr { hash : a -> U64 | a has Hash hash32 : a -> U32 | a has Hash - Id := U64 + Id := U64 has [Hash {hash, hash32}] hash = \@Id n -> n hash32 = \@Id n -> Num.toU32 n @@ -5985,7 +5985,7 @@ mod solve_expr { eq : a, a -> Bool | a has Ord le : a, a -> Bool | a has Ord - Id := U64 + Id := U64 has [Hash {hash, hash32}, Ord {eq, le}] hash = \@Id n -> n hash32 = \@Id n -> Num.toU32 n @@ -6013,7 +6013,7 @@ mod solve_expr { Hash has hash : a -> U64 | a has Hash - Id := U64 + Id := U64 has [Hash {hash}] hash : Id -> U64 hash = \@Id n -> n @@ -6033,7 +6033,7 @@ mod solve_expr { Hash has hash : a -> U64 | a has Hash - Id := U64 + Id := U64 has [Hash {hash}] hash : Id -> U64 "# @@ -6052,7 +6052,7 @@ mod solve_expr { Hash has hash : a -> U64 | a has Hash - Id := U64 + Id := U64 has [Hash {hash}] hash = \@Id n -> n @@ -6146,7 +6146,7 @@ mod solve_expr { hashEq = \x, y -> hash x == hash y - Id := U64 + Id := U64 has [Hash {hash}] hash = \@Id n -> n result = hashEq (@Id 100) (@Id 101) @@ -6331,15 +6331,13 @@ mod solve_expr { toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt - Linear := {} + Linear := {} has [Format {u8}] - # impl Format for Linear u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n) #^^{-1} - MyU8 := U8 + MyU8 := U8 has [Encoding {toEncoder}] - # impl Encoding for MyU8 toEncoder = \@MyU8 n -> u8 n #^^^^^^^^^{-1} @@ -6385,18 +6383,16 @@ mod solve_expr { Err e -> Err e - Linear := {} + Linear := {} has [DecoderFormatting {u8}] - # impl DecoderFormatting for Linear u8 = @Decoder \lst, @Linear {} -> #^^{-1} when List.first lst is Ok n -> { result: Ok n, rest: List.dropFirst lst } Err _ -> { result: Err TooShort, rest: [] } - MyU8 := U8 + MyU8 := U8 has [Decoder {decoder}] - # impl Decoding for MyU8 decoder = @Decoder \lst, fmt -> #^^^^^^^{-1} when decodeWith lst u8 fmt is @@ -6446,7 +6442,7 @@ mod solve_expr { Default has default : {} -> a | a has Default - A := {} + A := {} has [Default {default}] default = \{} -> @A {} main = @@ -6466,10 +6462,10 @@ mod solve_expr { indoc!( r#" app "test" - imports [Encode.{ toEncoder }, Json] + imports [Encode.{ Encoding, toEncoder }, Json] provides [main] to "./platform" - HelloWorld := {} + HelloWorld := {} has [Encoding {toEncoder}] toEncoder = \@HelloWorld {} -> Encode.custom \bytes, fmt -> @@ -6538,7 +6534,7 @@ mod solve_expr { Id has id : a -> a | a has Id - A := {} + A := {} has [Id {id}] id = \@A {} -> @A {} #^^{-1} @@ -6574,7 +6570,7 @@ mod solve_expr { Id1 has id1 : a -> a | a has Id1 Id2 has id2 : a -> a | a has Id2 - A := {} + A := {} has [Id1 {id1}, Id2 {id2}] id1 = \@A {} -> @A {} #^^^{-1} @@ -6607,7 +6603,7 @@ mod solve_expr { Id has id : a -> a | a has Id - A := {} + A := {} has [Id {id}] id = \@A {} -> @A {} #^^{-1} @@ -6645,7 +6641,7 @@ mod solve_expr { Id has id : a -> a | a has Id - A := {} + A := {} has [Id {id}] id = \@A {} -> @A {} #^^{-1} @@ -6681,7 +6677,7 @@ mod solve_expr { Id has id : a -> Thunk a | a has Id - A := {} + A := {} has [Id {id}] id = \@A {} -> \{} -> @A {} #^^{-1} @@ -6716,7 +6712,7 @@ mod solve_expr { Id has id : a -> Thunk a | a has Id - A := {} + A := {} has [Id {id}] id = \@A {} -> @Thunk (\{} -> @A {}) #^^{-1} @@ -6746,7 +6742,7 @@ mod solve_expr { Id has id : a -> Thunk a | a has Id - A := {} + A := {} has [Id {id}] id = \@A {} -> \{} -> @A {} #^^{-1} @@ -6775,7 +6771,7 @@ mod solve_expr { Diverge has diverge : a -> a | a has Diverge - A := {} + A := {} has [Diverge {diverge}] diverge = \@A {} -> diverge (@A {}) #^^^^^^^{-1} ^^^^^^^ @@ -6806,7 +6802,7 @@ mod solve_expr { ping : a -> a | a has Bounce pong : a -> a | a has Bounce - A := {} + A := {} has [Bounce {ping, pong}] ping = \@A {} -> pong (@A {}) #^^^^{-1} ^^^^ @@ -7109,11 +7105,11 @@ mod solve_expr { F has f : a -> (b -> {}) | a has F, b has G G has g : b -> {} | b has G - Fo := {} + Fo := {} has [F {f}] f = \@Fo {} -> g #^{-1} - Go := {} + Go := {} has [G {g}] g = \@Go {} -> {} #^{-1} @@ -7141,11 +7137,11 @@ mod solve_expr { F has f : a -> ({} -> b) | a has F, b has G G has g : {} -> b | b has G - Fo := {} + Fo := {} has [F {f}] f = \@Fo {} -> g #^{-1} - Go := {} + Go := {} has [G {g}] g = \{} -> @Go {} #^{-1} @@ -7177,11 +7173,11 @@ mod solve_expr { F has f : a -> (b -> {}) | a has F, b has G G has g : b -> {} | b has G - Fo := {} + Fo := {} has [F {f}] f = \@Fo {} -> g #^{-1} - Go := {} + Go := {} has [G {g}] g = \@Go {} -> {} #^{-1} @@ -7225,11 +7221,11 @@ mod solve_expr { F has f : a -> (b -> {}) | a has F, b has G G has g : b -> {} | b has G - Fo := {} + Fo := {} has [F {f}] f = \@Fo {} -> g #^{-1} - Go := {} + Go := {} has [G {g}] g = \@Go {} -> {} #^{-1} @@ -7260,11 +7256,11 @@ mod solve_expr { F has f : a, b -> ({} -> ({} -> {})) | a has F, b has G G has g : b -> ({} -> {}) | b has G - Fo := {} + Fo := {} has [F {f}] f = \@Fo {}, b -> \{} -> g b #^{-1} - Go := {} + Go := {} has [G {g}] g = \@Go {} -> \{} -> {} #^{-1} diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index a8182c3894..c82b723a4e 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -8367,35 +8367,6 @@ All branches in an `if` must have the same type! "# ); - test_report!( - ability_specialization_with_non_implementing_type, - indoc!( - r#" - app "test" provides [hash] to "./platform" - - Hash has hash : a -> Num.U64 | a has Hash - - hash = \{} -> 0u64 - "# - ), - @r#" - ── ILLEGAL SPECIALIZATION ──────────────────────────────── /code/proj/Main.roc ─ - - This specialization of `hash` is for a non-opaque type: - - 5│ hash = \{} -> 0u64 - ^^^^ - - It is specialized for - - {}a - - but structural types can never specialize abilities! - - Note: `hash` is a member of `#UserApp.Hash` - "# - ); - test_report!( ability_specialization_does_not_match_type, indoc!( @@ -8404,7 +8375,7 @@ All branches in an `if` must have the same type! Hash has hash : a -> U64 | a has Hash - Id := U32 + Id := U32 has [Hash {hash}] hash = \@Id n -> n "# @@ -8437,7 +8408,7 @@ All branches in an `if` must have the same type! eq : a, a -> Bool | a has Eq le : a, a -> Bool | a has Eq - Id := U64 + Id := U64 has [Eq {eq}] eq = \@Id m, @Id n -> m == n "# @@ -8456,7 +8427,7 @@ All branches in an `if` must have the same type! ); test_report!( - ability_specialization_overly_generalized, + ability_specialization_is_unused, indoc!( r#" app "test" provides [hash] to "./platform" @@ -8467,29 +8438,78 @@ All branches in an `if` must have the same type! hash = \_ -> 0u64 "# ), - @r#" - ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + @r###" + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - This specialization of `hash` is overly general: + `hash` is not used anywhere in your code. - 6│ hash = \_ -> 0u64 - ^^^^ + 6│ hash = \_ -> 0u64 + ^^^^ - This value is a declared specialization of type: + If you didn't intend on using `hash` then remove it so future readers of + your code don't wonder why it is there. + "### + ); - a -> U64 + test_report!( + ability_specialization_is_duplicated, + indoc!( + r#" + app "test" provides [hash, One, Two] to "./platform" - But the type annotation on `hash` says it must match: + Hash has + hash : a -> U64 | a has Hash - a -> U64 | a has Hash + One := {} has [Hash {hash}] + Two := {} has [Hash {hash}] - Note: The specialized type is too general, and does not provide a - concrete type where a type variable is bound to an ability. + hash = \_ -> 0u64 + "# + ), + // TODO: the error message here could be seriously improved! + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ - Specializations can only be made for concrete types. If you have a - generic implementation for this value, perhaps you don't need an - ability? - "# + This specialization of `hash` is overly general: + + 9│ hash = \_ -> 0u64 + ^^^^ + + This value is a declared specialization of type: + + a -> U64 + + But the type annotation on `hash` says it must match: + + a -> U64 | a has Hash + + Note: The specialized type is too general, and does not provide a + concrete type where a type variable is bound to an ability. + + Specializations can only be made for concrete types. If you have a + generic implementation for this value, perhaps you don't need an + ability? + "### + ); + + test_report!( + #[ignore = "TODO does not error yet"] + ability_specialization_is_duplicated_with_type_mismatch, + indoc!( + r#" + app "test" provides [hash, One, Two] to "./platform" + + Hash has + hash : a -> U64 | a has Hash + + One := {} has [Hash {hash}] + Two := {} has [Hash {hash}] + + hash = \@One _ -> 0u64 + "# + ), + @r###" + "### ); test_report!( @@ -8501,7 +8521,7 @@ All branches in an `if` must have the same type! Eq has eq : a, a -> Bool | a has Eq - You := {} + You := {} has [Eq {eq}] AndI := {} eq = \@You {}, @AndI {} -> False @@ -8539,7 +8559,7 @@ All branches in an `if` must have the same type! Hash has hash : a -> U64 | a has Hash - Id := U64 + Id := U64 has [Hash {hash}] hash : Id -> U32 hash = \@Id n -> n @@ -8588,7 +8608,7 @@ All branches in an `if` must have the same type! Hash has hash : a -> U64 | a has Hash - Id := U64 + Id := U64 has [Hash {hash}] hash = \@Id n -> n @@ -8666,7 +8686,7 @@ All branches in an `if` must have the same type! Hash has hash : a -> U64 | a has Hash - Id := U64 + Id := U64 has [Hash {hash}] hash = \@Id n -> n hashable : a | a has Hash @@ -8879,7 +8899,7 @@ All branches in an `if` must have the same type! Default has default : {} -> a | a has Default main = - A := {} + A := {} has [Default {default}] default = \{} -> @A {} default {} "# @@ -9423,40 +9443,6 @@ All branches in an `if` must have the same type! "# ); - test_report!( - has_encoding_dominated_by_custom, - indoc!( - r#" - app "test" imports [Encode.{ Encoding, toEncoder, custom }] provides [A] to "./platform" - - A := {} has [Encode.Encoding] - - toEncoder = \@A {} -> custom \l, _ -> l - "# - ), - @r#" - ── CONFLICTING DERIVE AND IMPLEMENTATION ───────────────── /code/proj/Main.roc ─ - - `A` both derives and custom-implements `Encode.Encoding`. We found the - derive here: - - 3│ A := {} has [Encode.Encoding] - ^^^^^^^^^^^^^^^ - - and one custom implementation of `Encode.Encoding` here: - - 5│ toEncoder = \@A {} -> custom \l, _ -> l - ^^^^^^^^^ - - Derived and custom implementations can conflict, so one of them needs - to be removed! - - Note: We'll try to compile your program using the custom - implementation first, and fall-back on the derived implementation if - needed. Make sure to disambiguate which one you want! - "# - ); - test_report!( issue_1755, indoc!(