mirror of
https://github.com/unisonweb/unison.git
synced 2024-09-11 02:25:39 +03:00
don't expect constructors of builtin types
This commit is contained in:
parent
ad1c9b9f98
commit
0299f38946
@ -26,7 +26,7 @@ import Unison.Merge.Unconflicts (Unconflicts (..))
|
||||
import Unison.Merge.Unconflicts qualified as Unconflicts
|
||||
import Unison.Name (Name)
|
||||
import Unison.Prelude hiding (catMaybes)
|
||||
import Unison.Reference (TermReference, TermReferenceId, TypeReference, TypeReferenceId)
|
||||
import Unison.Reference (Reference' (..), TermReference, TermReferenceId, TypeReference, TypeReferenceId)
|
||||
import Unison.Reference qualified as Reference
|
||||
import Unison.Referent (Referent)
|
||||
import Unison.Referent qualified as Referent
|
||||
@ -72,12 +72,13 @@ combineDiffs declNameLookups defns diffs = do
|
||||
Right (conflicts, unconflicts)
|
||||
|
||||
identifyConflicts ::
|
||||
HasCallStack =>
|
||||
TwoWay DeclNameLookup ->
|
||||
TwoWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) ->
|
||||
DefnsF2 (Map Name) CombinedDiffOps Referent TypeReference ->
|
||||
TwoWay (DefnsF (Map Name) TermReference TypeReference)
|
||||
identifyConflicts declNameLookups defns =
|
||||
\diff -> loop (makeInitialIdentifyConflictsState diff)
|
||||
loop . makeInitialIdentifyConflictsState
|
||||
where
|
||||
loop :: S -> TwoWay (DefnsF (Map Name) TermReference TypeReference)
|
||||
loop s =
|
||||
@ -102,7 +103,9 @@ identifyConflicts declNameLookups defns =
|
||||
Just $
|
||||
s
|
||||
& myTypeConflicts_ .~ conflicts
|
||||
& theirTermStack_ %~ (expectConstructorNames myDeclNameLookup name ++)
|
||||
& case ref of
|
||||
ReferenceBuiltin _ -> id -- builtin types don't have constructors
|
||||
ReferenceDerived _ -> theirTermStack_ %~ (expectConstructorNames myDeclNameLookup name ++)
|
||||
|
||||
me_ :: Lens' (TwoWay a) a
|
||||
me_ = TwoWay.who_ s.me
|
||||
|
@ -40,13 +40,13 @@ data DeclNameLookup = DeclNameLookup
|
||||
deriving stock (Generic)
|
||||
deriving (Semigroup) via (GenericSemigroupMonoid DeclNameLookup)
|
||||
|
||||
expectDeclName :: DeclNameLookup -> Name -> Name
|
||||
expectDeclName :: HasCallStack => DeclNameLookup -> Name -> Name
|
||||
expectDeclName DeclNameLookup {constructorToDecl} x =
|
||||
case Map.lookup x constructorToDecl of
|
||||
Nothing -> error (reportBug "E246726" ("Expected constructor name key " <> show x <> " in decl name lookup"))
|
||||
Just y -> y
|
||||
|
||||
expectConstructorNames :: DeclNameLookup -> Name -> [Name]
|
||||
expectConstructorNames :: HasCallStack => DeclNameLookup -> Name -> [Name]
|
||||
expectConstructorNames DeclNameLookup {declToConstructors} x =
|
||||
case Map.lookup x declToConstructors of
|
||||
Nothing -> error (reportBug "E077058" ("Expected decl name key " <> show x <> " in decl name lookup"))
|
||||
|
@ -1,6 +1,422 @@
|
||||
# tests for the new merge command
|
||||
# The `merge` command
|
||||
|
||||
## Basic fast-forward merge
|
||||
The `merge` command merges together two branches in the same project: the current branch (unspecificed), and the target
|
||||
branch. For example, to merge `topic` into `main`, switch to `main` and run `merge topic`. Let's see a simple,
|
||||
unconflicted merge in action, wherein Alice (us) and Bob (them) have added different terms.
|
||||
|
||||
## Basic merge
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "alices foo"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> add
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
bar : Text
|
||||
bar = "bobs bar"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/bob> add
|
||||
project/alice> merge2 /bob
|
||||
project/alice> view foo bar
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## Update propagation
|
||||
|
||||
Updates are propagated. In this example, Alice updates `foo`, and Bob adds a new dependent `bar` of (the old) `foo`.
|
||||
When Bob's branch is merged into Alice's, her update to `foo` is propagated to `bar`.
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "old foo"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> add
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "new foo"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> update
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
bar : Text
|
||||
bar = foo ++ foo
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/bob> add
|
||||
project/alice> merge2 /bob
|
||||
project/alice> view foo bar
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## Update propagation with common dependent
|
||||
|
||||
Different hashes don't necessarily imply an update. In this example, Alice and Bob both update different dependencies
|
||||
`bar` and `baz` of a common dependent `foo`, so their `foo`s have different hashes. However, we can merge these changes
|
||||
together just fine, resulting in a `foo` that incorporates both updates.
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "foo" ++ bar ++ baz
|
||||
|
||||
bar : Text
|
||||
bar = "old bar"
|
||||
|
||||
baz : Text
|
||||
baz = "old baz"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> add
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
bar : Text
|
||||
bar = "alices bar"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> update
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
baz : Text
|
||||
baz = "bobs baz"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/bob> update
|
||||
project/alice> merge2 /bob
|
||||
project/alice> view foo bar baz
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## Typechecking failure
|
||||
|
||||
Alice's update may fail to typecheck when propagating to Bob's dependents.
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "foo"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> add
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Nat
|
||||
foo = 100
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> update
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
bar : Text
|
||||
bar = foo ++ foo
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
project/bob> update
|
||||
project/alice> merge2 /bob
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## Simple term conflict
|
||||
|
||||
Alice and Bob may disagree about the definition of a term. In this case, the conflicted term and all of its dependents
|
||||
are given to the user to resolve. The unconflicted parts of a merge (and any merge with conflicts in general) are put
|
||||
into the namespace.
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "old foo"
|
||||
|
||||
bar : Text
|
||||
bar = "old bar"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> add
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "alices foo"
|
||||
|
||||
bar : Text
|
||||
bar = "alices bar"
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> update
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Text
|
||||
foo = "bobs foo"
|
||||
|
||||
baz : Text
|
||||
baz = "bobs baz"
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
project/bob> update
|
||||
project/alice> merge2 /bob
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/merge-bob-into-alice> view bar baz
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## Simple type conflict
|
||||
|
||||
Ditto for types; if the hashes don't match, it's a conflict. In this example, Alice and Bob do different things to the
|
||||
same constructor. However, any explicit changes to the same type will result in a conflict, including changes that could
|
||||
concievably be merged (e.g. Alice and Bob both add a new constructor, or edit different constructors).
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```unison
|
||||
unique type Foo = MkFoo Nat
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> add
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
unique type Foo = MkFoo Nat Nat
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> update
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
unique type Foo = MkFoo Nat Text
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
project/bob> update
|
||||
project/alice> merge2 /bob
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
## Term conflict with a constructor
|
||||
|
||||
In this example, Alice updates a type, while Bob "updates" one of the constructors (by changing it to a term), and adds
|
||||
back a name for the constructor somewhere else. Bob didn't actually update the type itself, but there is nonetheless
|
||||
a conflict between Alice's type (due to one of its constructors) and Bob's term.
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```unison
|
||||
unique type Foo
|
||||
= MkFooOne Nat
|
||||
| MkFooTwo Nat Nat
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> add
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
unique type Foo
|
||||
= MkFooOne Nat Text
|
||||
| MkFooTwo Nat Nat
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> update
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
unique type Foo
|
||||
= MkFooOne Nat
|
||||
| MkFooTwoRenamed Nat Nat
|
||||
|
||||
Foo.MkFooTwo : Text
|
||||
Foo.MkFooTwo = "hello"
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
project/bob> update
|
||||
project/alice> merge2 /bob
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
|
||||
## Precondition violations
|
||||
|
||||
Let's see a number of merge precondition violations. These are conditions under which we can't perform a merge, and the
|
||||
user will have to fix up the namespace(s) manually before attempting to merge again.
|
||||
|
||||
### Conflicted aliases
|
||||
|
||||
If `foo` and `bar` are aliases in the nearest common ancestor, but not in Alice's branch, then we don't know whether to
|
||||
update Bob's dependents to Alice's `foo` or Alice's `bar` (and vice-versa).
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Nat
|
||||
foo = 100
|
||||
|
||||
bar : Nat
|
||||
bar = 100
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> add
|
||||
project/main> branch alice
|
||||
```
|
||||
|
||||
```unison
|
||||
foo : Nat
|
||||
foo = 200
|
||||
|
||||
bar : Nat
|
||||
bar = 300
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/alice> update
|
||||
project/main> branch bob
|
||||
```
|
||||
|
||||
```unison
|
||||
baz : Text
|
||||
baz = "baz"
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
project/bob> add
|
||||
project/alice> merge2 /bob
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
### Conflict involving builtin
|
||||
|
||||
We don't have a way of rendering a builtin in a scratch file, where users resolve merge conflicts. Thus, if there is a
|
||||
conflict involving a builtin, we can't perform a merge.
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty project
|
||||
project/main> builtins.mergeio
|
||||
```
|
||||
|
||||
```ucm
|
||||
project/main> branch topic
|
||||
project/main> alias.type builtin.Nat MyNat
|
||||
project/topic>
|
||||
```
|
||||
|
||||
```unison
|
||||
unique type MyNat = MyNat Nat
|
||||
```
|
||||
|
||||
```ucm:error
|
||||
project/topic> add
|
||||
project/main> merge2 /topic
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> project.delete project
|
||||
```
|
||||
|
||||
# fast-forward
|
||||
|
||||
```ucm:hide
|
||||
.> project.create-empty proj
|
||||
@ -15,7 +431,7 @@ foo = 1
|
||||
```ucm
|
||||
proj/main> add
|
||||
proj/main> branch topic
|
||||
proj/topic>
|
||||
proj/topic>
|
||||
```
|
||||
|
||||
```unison
|
||||
@ -49,7 +465,7 @@ foo = 1
|
||||
|
||||
```ucm
|
||||
proj/topic> add
|
||||
proj/main>
|
||||
proj/main>
|
||||
```
|
||||
|
||||
```unison
|
||||
@ -85,7 +501,7 @@ foo = 1
|
||||
|
||||
```ucm
|
||||
proj/topic> add
|
||||
proj/main>
|
||||
proj/main>
|
||||
```
|
||||
|
||||
```unison
|
||||
@ -353,7 +769,7 @@ foo = 1
|
||||
```ucm
|
||||
proj/main> add
|
||||
proj/main> branch topic
|
||||
proj/topic>
|
||||
proj/topic>
|
||||
```
|
||||
|
||||
`topic` adds a dependent of `foo`
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user