Repository: tweag/nickel (rust)

Issue:
`default` values and contracts don’t compose properly
**Describe the bug**

When a value `bar` has a contract and a default value, and its default value itself has both a contract and a default value, then the default of the default value will be ignored, and only its contract will be applied.

**To Reproduce**

Evaluating

```
let
  myContract = { x | Str }
in
{
  foo | #myContract | default = { x = "From foo" },
  bar | #{..} | default = foo
}
```

results in

```
error: empty metavalue
  ┌─ /tmp/tmp.eYEzU3ur1v/test.ncl:4:18
  │
4 │   myContract = { x | Str }
  │                  ^^^^^^^ here
```

rather than the expected

```
{
  "bar": {
    "x": "From foo"
  },
  "foo": {
    "x": "From foo"
  }
}
```

Everything works fine if `bar` either doesn’t have a contract or has a plain type annotation (`bar | { x : Str } | default = foo`).


**Environment**
 - OS name + version: NixOS 21.05
 - Version of the code: 8ce9a90

/cc @yannham 

Pull request description:
[Fix]Make MergeContract operator strict in its arguments
Fix #402.

When using merge for the application of a record contract, we should ignore top-level meta-data such as a default priority which can otherwise cause inner default values of the tested value to be silently dropped (see #402 for an example). That is, when merge is used for a contract-as-a-record application, it must be strict in its arguments with respect to metavalues.

Patch:
diff --git a/src/term.rs b/src/term.rs
index da56fdb6..8063bc62 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -810,7 +810,6 @@ impl NAryOp {
 
     pub fn is_strict(&self) -> bool {
         match self {
-            NAryOp::MergeContract() => false,
             _ => true,
         }
     }


Test patch:
diff --git a/tests/pass/contracts.ncl b/tests/pass/contracts.ncl
index 0e89ebbc..e3730e04 100644
--- a/tests/pass/contracts.ncl
+++ b/tests/pass/contracts.ncl
@@ -100,4 +100,12 @@ f `boo == 3) &&
 (({foo : {bar: Bool} | default = {bar = false}} & {foo.bar = true}).foo.bar
   | #Assert) &&
 
+// nested_metavalues
+// Regression test for #402
+(let myContract = { x | Str } in
+{
+  foo | #myContract | default = { x = "From foo" },
+  bar | #{..} | default = foo
+} == { foo.x = "From foo", bar.x = "From foo"} | #Assert) &&
+
 true