# This issue addresses evaluation in nested lets.
-- nested.cue --
issue3801: let: reduced: {
	let F = {
		base: {
			in: string
			let X = {a: "\(in)" }.a
			out: X
		}
		XXX: base & {in: "foo"} // failure is here
	}
	z: F.XXX.out
}
issue3801: nonRooted: reduced: {
	#Output: output: {
		#base: {
			val0: string
			msgObj: {a: val0}.a
			value: msgObj
		}
		out: #base & {val0: "foo"}
	}
	out: (#Output & {}).output // non-concrete
}
issue3801: let: full: {
	#Resource: {
		someMsg: string
		obs: {} | *{missing: true}
	
		let pickMsg = [
			if obs.missing {msg: "\(someMsg)"},
			{msg: "bar"},
		][0]
		patches: [{
			op:    "add"
			path:  "/metadata"
			value: pickMsg.msg
		}]
	}
	
	#Patches: [string]: _
	#JSONPatch: {
		namespace?: string
		patch: [...#JSONOp]
		output: #Patches & {(namespace): patch}
	}
	#JSONOp: {
		op:    "add"
		path:  string
		value: _
	} | {
		op:   "remove"
		path: string
	}
	
	#Main: {
		NS=namespace: string
	
		output: jsonPatch.output
	
		let jsonPatch = #JSONPatch & {
			let base = #Resource & {}
			let withMsg = base & {someMsg: "foo"}
	
			namespace: NS
	
			patch: withMsg.patches
		}
	}
	out: (#Main & {namespace: "ns1"}).output
}
issue3801: nonRooted: full: {
	#Output: {
		NS=namespace: string
		output: (#SubOutput & {
			namespace: NS
			let base2 = {
				let base = #Base & {}
				output: base & {val0: "foo"}
			}
			patch: base2.output.value
		}).output
	}
	out: (#Output & {namespace: "ns1"}).output
	
	#SubOutput: {
		namespace?: string
	
		patch: _
		output: (namespace): patch
	}
	
	#Base: {
		val0: string
		let msgObj = [val0][0]
		value: msgObj
	}
}
-- out/evalalpha/stats --
Leaks:  266
Freed:  0
Reused: 0
Allocs: 266
Retain: 0

Unifications: 181
Conjuncts:    346
Disjuncts:    16

CloseIDElems: 155
NumCloseIDs: 83
-- diff/-out/evalalpha/stats<==>+out/eval/stats --
diff old new
--- old
+++ new
@@ -1,9 +1,12 @@
-Leaks:  237
-Freed:  408
-Reused: 402
-Allocs: 243
-Retain: 430
-
-Unifications: 599
-Conjuncts:    1319
-Disjuncts:    762
+Leaks:  266
+Freed:  0
+Reused: 0
+Allocs: 266
+Retain: 0
+
+Unifications: 181
+Conjuncts:    346
+Disjuncts:    16
+
+CloseIDElems: 155
+NumCloseIDs: 83
-- out/eval/stats --
Leaks:  237
Freed:  408
Reused: 402
Allocs: 243
Retain: 430

Unifications: 599
Conjuncts:    1319
Disjuncts:    762
-- out/evalalpha --
(struct){
  issue3801: (struct){
    let: (struct){
      reduced: (struct){
        let F#1 = (struct){
          base: (struct){
            in: (string){ string }
            let X#2 = (_|_){
              // [incomplete] issue3801.let.reduced.F.base.X: invalid interpolation: non-concrete value string (type string):
              //     ./nested.cue:5:16
              //     ./nested.cue:4:8
            }
            out: (_|_){
              // [incomplete] issue3801.let.reduced.F.base.out: invalid interpolation: non-concrete value string (type string):
              //     ./nested.cue:5:16
              //     ./nested.cue:4:8
            }
          }
          XXX: (struct){
            in: (string){ "foo" }
            let X#2 = (string){ "foo" }
            out: (string){ "foo" }
          }
        }
        z: (string){ "foo" }
      }
      full: (struct){
        #Resource: (#struct){
          someMsg: (string){ string }
          obs: (#struct){ |(*(#struct){
              missing: (bool){ true }
            }, (#struct){
            }) }
          let pickMsg#4 = (#struct){
            msg: (_|_){
              // [incomplete] issue3801.let.full.#Resource.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
              //     ./nested.cue:29:25
              //     ./nested.cue:25:12
            }
          }
          patches: (#list){
            0: (#struct){
              op: (string){ "add" }
              path: (string){ "/metadata" }
              value: (_|_){
                // [incomplete] issue3801.let.full.#Resource.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
                //     ./nested.cue:29:25
                //     ./nested.cue:25:12
              }
            }
          }
        }
        #Patches: (#struct){
        }
        #JSONPatch: (#struct){
          namespace?: (string){ string }
          patch: (list){
          }
          output: (_|_){
            // [incomplete] issue3801.let.full.#JSONPatch.output: cannot reference optional field: namespace:
            //     ./nested.cue:43:24
          }
        }
        #JSONOp: (#struct){ |((#struct){
            op: (string){ "add" }
            path: (string){ string }
            value: (_){ _ }
          }, (#struct){
            op: (string){ "remove" }
            path: (string){ string }
          }) }
        #Main: (#struct){
          namespace: (string){ string }
          output: (_|_){
            // [incomplete] issue3801.let.full.#Main.jsonPatch.output: key value of dynamic field must be concrete, found string:
            //     ./nested.cue:43:24
            //     ./nested.cue:41:15
          }
          let jsonPatch#5 = (#struct){
            let base#8 = (#struct){
              someMsg: (string){ string }
              obs: (#struct){ |(*(#struct){
                  missing: (bool){ true }
                }, (#struct){
                }) }
              let pickMsg#4 = (#struct){
                msg: (_|_){
                  // [incomplete] issue3801.let.full.#Main.jsonPatch.base.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
                  //     ./nested.cue:29:25
                  //     ./nested.cue:25:12
                }
              }
              patches: (#list){
                0: (#struct){
                  op: (string){ "add" }
                  path: (string){ "/metadata" }
                  value: (_|_){
                    // [incomplete] issue3801.let.full.#Main.jsonPatch.base.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
                    //     ./nested.cue:29:25
                    //     ./nested.cue:25:12
                  }
                }
              }
            }
            let withMsg#9 = (#struct){
              someMsg: (string){ "foo" }
              obs: (#struct){ |(*(#struct){
                  missing: (bool){ true }
                }, (#struct){
                }) }
              let pickMsg#4 = (#struct){
                msg: (string){ "foo" }
              }
              patches: (#list){
                0: (#struct){
                  op: (string){ "add" }
                  path: (string){ "/metadata" }
                  value: (string){ "foo" }
                }
              }
            }
            namespace: (string){ string }
            patch: (#list){
              0: (#struct){
                op: (string){ "add" }
                path: (string){ "/metadata" }
                value: (string){ "foo" }
              }
            }
            output: (_|_){
              // [incomplete] issue3801.let.full.#Main.jsonPatch.output: key value of dynamic field must be concrete, found string:
              //     ./nested.cue:43:24
              //     ./nested.cue:41:15
            }
          }
        }
        out: (#struct){
          ns1: (#list){
            0: (#struct){
              op: (string){ "add" }
              path: (string){ "/metadata" }
              value: (string){ "foo" }
            }
          }
        }
      }
    }
    nonRooted: (struct){
      reduced: (struct){
        #Output: (#struct){
          output: (#struct){
            #base: (#struct){
              val0: (string){ string }
              msgObj: (string){ string }
              value: (string){ string }
            }
            out: (#struct){
              val0: (string){ "foo" }
              msgObj: (string){ "foo" }
              value: (string){ "foo" }
            }
          }
        }
        out: (#struct){
          #base: (#struct){
            val0: (string){ string }
            msgObj: (string){ string }
            value: (string){ string }
          }
          out: (#struct){
            val0: (string){ "foo" }
            msgObj: (string){ "foo" }
            value: (string){ "foo" }
          }
        }
      }
      full: (struct){
        #Output: (#struct){
          namespace: (string){ string }
          output: (_|_){
            // [incomplete] issue3801.nonRooted.full.#Output.output: key value of dynamic field must be concrete, found string:
            //     ./nested.cue:88:12
            //     ./nested.cue:85:15
          }
        }
        out: (#struct){
          ns1: (string){ "foo" }
        }
        #SubOutput: (#struct){
          namespace?: (string){ string }
          patch: (_){ _ }
          output: (_|_){
            // [incomplete] issue3801.nonRooted.full.#SubOutput.output: cannot reference optional field: namespace:
            //     ./nested.cue:88:12
          }
        }
        #Base: (#struct){
          val0: (string){ string }
          let msgObj#D = (string){ string }
          value: (string){ string }
        }
      }
    }
  }
}
-- diff/-out/evalalpha<==>+out/eval --
diff old new
--- old
+++ new
@@ -43,7 +43,7 @@
               op: (string){ "add" }
               path: (string){ "/metadata" }
               value: (_|_){
-                // [incomplete] issue3801.let.full.#Resource.patches.0.value: invalid interpolation: non-concrete value string (type string):
+                // [incomplete] issue3801.let.full.#Resource.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
                 //     ./nested.cue:29:25
                 //     ./nested.cue:25:12
               }
@@ -72,38 +72,11 @@
         #Main: (#struct){
           namespace: (string){ string }
           output: (_|_){
-            // [incomplete] issue3801.let.full.#Main.output: invalid non-ground value string (must be concrete string):
+            // [incomplete] issue3801.let.full.#Main.jsonPatch.output: key value of dynamic field must be concrete, found string:
+            //     ./nested.cue:43:24
             //     ./nested.cue:41:15
-            //     ./nested.cue:63:15
-            _: (#list){
-              0: (#struct){
-                op: (string){ "add" }
-                path: (string){ "/metadata" }
-                value: (string){ "foo" }
-              }
-            }
           }
           let jsonPatch#5 = (#struct){
-            namespace: (string){ string }
-            patch: (#list){
-              0: (#struct){
-                op: (string){ "add" }
-                path: (string){ "/metadata" }
-                value: (string){ "foo" }
-              }
-            }
-            output: (_|_){
-              // [incomplete] issue3801.let.full.#Main.jsonPatch.output: invalid non-ground value string (must be concrete string):
-              //     ./nested.cue:41:15
-              //     ./nested.cue:63:15
-              _: (#list){
-                0: (#struct){
-                  op: (string){ "add" }
-                  path: (string){ "/metadata" }
-                  value: (string){ "foo" }
-                }
-              }
-            }
             let base#8 = (#struct){
               someMsg: (string){ string }
               obs: (#struct){ |(*(#struct){
@@ -122,7 +95,7 @@
                   op: (string){ "add" }
                   path: (string){ "/metadata" }
                   value: (_|_){
-                    // [incomplete] issue3801.let.full.#Main.jsonPatch.base.patches.0.value: invalid interpolation: non-concrete value string (type string):
+                    // [incomplete] issue3801.let.full.#Main.jsonPatch.base.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
                     //     ./nested.cue:29:25
                     //     ./nested.cue:25:12
                   }
@@ -146,6 +119,19 @@
                 }
               }
             }
+            namespace: (string){ string }
+            patch: (#list){
+              0: (#struct){
+                op: (string){ "add" }
+                path: (string){ "/metadata" }
+                value: (string){ "foo" }
+              }
+            }
+            output: (_|_){
+              // [incomplete] issue3801.let.full.#Main.jsonPatch.output: key value of dynamic field must be concrete, found string:
+              //     ./nested.cue:43:24
+              //     ./nested.cue:41:15
+            }
           }
         }
         out: (#struct){
@@ -175,7 +161,7 @@
             }
           }
         }
-        out: (struct){
+        out: (#struct){
           #base: (#struct){
             val0: (string){ string }
             msgObj: (string){ string }
@@ -192,13 +178,12 @@
         #Output: (#struct){
           namespace: (string){ string }
           output: (_|_){
-            // [incomplete] issue3801.nonRooted.full.#Output.output: invalid non-ground value string (must be concrete string):
+            // [incomplete] issue3801.nonRooted.full.#Output.output: key value of dynamic field must be concrete, found string:
+            //     ./nested.cue:88:12
             //     ./nested.cue:85:15
-            //     ./nested.cue:74:15
-            _: (string){ "foo" }
-          }
-        }
-        out: (struct){
+          }
+        }
+        out: (#struct){
           ns1: (string){ "foo" }
         }
         #SubOutput: (#struct){
-- out/eval --
(struct){
  issue3801: (struct){
    let: (struct){
      reduced: (struct){
        let F#1 = (struct){
          base: (struct){
            in: (string){ string }
            let X#2 = (_|_){
              // [incomplete] issue3801.let.reduced.F.base.X: invalid interpolation: non-concrete value string (type string):
              //     ./nested.cue:5:16
              //     ./nested.cue:4:8
            }
            out: (_|_){
              // [incomplete] issue3801.let.reduced.F.base.out: invalid interpolation: non-concrete value string (type string):
              //     ./nested.cue:5:16
              //     ./nested.cue:4:8
            }
          }
          XXX: (struct){
            in: (string){ "foo" }
            let X#2 = (string){ "foo" }
            out: (string){ "foo" }
          }
        }
        z: (string){ "foo" }
      }
      full: (struct){
        #Resource: (#struct){
          someMsg: (string){ string }
          obs: (#struct){ |(*(#struct){
              missing: (bool){ true }
            }, (#struct){
            }) }
          let pickMsg#4 = (#struct){
            msg: (_|_){
              // [incomplete] issue3801.let.full.#Resource.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
              //     ./nested.cue:29:25
              //     ./nested.cue:25:12
            }
          }
          patches: (#list){
            0: (#struct){
              op: (string){ "add" }
              path: (string){ "/metadata" }
              value: (_|_){
                // [incomplete] issue3801.let.full.#Resource.patches.0.value: invalid interpolation: non-concrete value string (type string):
                //     ./nested.cue:29:25
                //     ./nested.cue:25:12
              }
            }
          }
        }
        #Patches: (#struct){
        }
        #JSONPatch: (#struct){
          namespace?: (string){ string }
          patch: (list){
          }
          output: (_|_){
            // [incomplete] issue3801.let.full.#JSONPatch.output: cannot reference optional field: namespace:
            //     ./nested.cue:43:24
          }
        }
        #JSONOp: (#struct){ |((#struct){
            op: (string){ "add" }
            path: (string){ string }
            value: (_){ _ }
          }, (#struct){
            op: (string){ "remove" }
            path: (string){ string }
          }) }
        #Main: (#struct){
          namespace: (string){ string }
          output: (_|_){
            // [incomplete] issue3801.let.full.#Main.output: invalid non-ground value string (must be concrete string):
            //     ./nested.cue:41:15
            //     ./nested.cue:63:15
            _: (#list){
              0: (#struct){
                op: (string){ "add" }
                path: (string){ "/metadata" }
                value: (string){ "foo" }
              }
            }
          }
          let jsonPatch#5 = (#struct){
            namespace: (string){ string }
            patch: (#list){
              0: (#struct){
                op: (string){ "add" }
                path: (string){ "/metadata" }
                value: (string){ "foo" }
              }
            }
            output: (_|_){
              // [incomplete] issue3801.let.full.#Main.jsonPatch.output: invalid non-ground value string (must be concrete string):
              //     ./nested.cue:41:15
              //     ./nested.cue:63:15
              _: (#list){
                0: (#struct){
                  op: (string){ "add" }
                  path: (string){ "/metadata" }
                  value: (string){ "foo" }
                }
              }
            }
            let base#8 = (#struct){
              someMsg: (string){ string }
              obs: (#struct){ |(*(#struct){
                  missing: (bool){ true }
                }, (#struct){
                }) }
              let pickMsg#4 = (#struct){
                msg: (_|_){
                  // [incomplete] issue3801.let.full.#Main.jsonPatch.base.pickMsg.msg: invalid interpolation: non-concrete value string (type string):
                  //     ./nested.cue:29:25
                  //     ./nested.cue:25:12
                }
              }
              patches: (#list){
                0: (#struct){
                  op: (string){ "add" }
                  path: (string){ "/metadata" }
                  value: (_|_){
                    // [incomplete] issue3801.let.full.#Main.jsonPatch.base.patches.0.value: invalid interpolation: non-concrete value string (type string):
                    //     ./nested.cue:29:25
                    //     ./nested.cue:25:12
                  }
                }
              }
            }
            let withMsg#9 = (#struct){
              someMsg: (string){ "foo" }
              obs: (#struct){ |(*(#struct){
                  missing: (bool){ true }
                }, (#struct){
                }) }
              let pickMsg#4 = (#struct){
                msg: (string){ "foo" }
              }
              patches: (#list){
                0: (#struct){
                  op: (string){ "add" }
                  path: (string){ "/metadata" }
                  value: (string){ "foo" }
                }
              }
            }
          }
        }
        out: (#struct){
          ns1: (#list){
            0: (#struct){
              op: (string){ "add" }
              path: (string){ "/metadata" }
              value: (string){ "foo" }
            }
          }
        }
      }
    }
    nonRooted: (struct){
      reduced: (struct){
        #Output: (#struct){
          output: (#struct){
            #base: (#struct){
              val0: (string){ string }
              msgObj: (string){ string }
              value: (string){ string }
            }
            out: (#struct){
              val0: (string){ "foo" }
              msgObj: (string){ "foo" }
              value: (string){ "foo" }
            }
          }
        }
        out: (struct){
          #base: (#struct){
            val0: (string){ string }
            msgObj: (string){ string }
            value: (string){ string }
          }
          out: (#struct){
            val0: (string){ "foo" }
            msgObj: (string){ "foo" }
            value: (string){ "foo" }
          }
        }
      }
      full: (struct){
        #Output: (#struct){
          namespace: (string){ string }
          output: (_|_){
            // [incomplete] issue3801.nonRooted.full.#Output.output: invalid non-ground value string (must be concrete string):
            //     ./nested.cue:85:15
            //     ./nested.cue:74:15
            _: (string){ "foo" }
          }
        }
        out: (struct){
          ns1: (string){ "foo" }
        }
        #SubOutput: (#struct){
          namespace?: (string){ string }
          patch: (_){ _ }
          output: (_|_){
            // [incomplete] issue3801.nonRooted.full.#SubOutput.output: cannot reference optional field: namespace:
            //     ./nested.cue:88:12
          }
        }
        #Base: (#struct){
          val0: (string){ string }
          let msgObj#D = (string){ string }
          value: (string){ string }
        }
      }
    }
  }
}
-- out/compile --
--- nested.cue
{
  issue3801: {
    let: {
      reduced: {
        let F#1 = {
          base: {
            in: string
            let X#2 = {
              a: "\(〈1;in〉)"
            }.a
            out: 〈0;let X#2〉
          }
          XXX: (〈0;base〉 & {
            in: "foo"
          })
        }
        z: 〈0;let F#1〉.XXX.out
      }
    }
  }
  issue3801: {
    nonRooted: {
      reduced: {
        #Output: {
          output: {
            #base: {
              val0: string
              msgObj: {
                a: 〈1;val0〉
              }.a
              value: 〈0;msgObj〉
            }
            out: (〈0;#base〉 & {
              val0: "foo"
            })
          }
        }
        out: (〈0;#Output〉 & {}).output
      }
    }
  }
  issue3801: {
    let: {
      full: {
        #Resource: {
          someMsg: string
          obs: ({}|*{
            missing: true
          })
          let pickMsg#4 = [
            if 〈1;obs〉.missing {
              msg: "\(〈2;someMsg〉)"
            },
            {
              msg: "bar"
            },
          ][0]
          patches: [
            {
              op: "add"
              path: "/metadata"
              value: 〈2;let pickMsg#4〉.msg
            },
          ]
        }
        #Patches: {
          [string]: _
        }
        #JSONPatch: {
          namespace?: string
          patch: [
            ...〈2;#JSONOp〉,
          ]
          output: (〈1;#Patches〉 & {
            〈1;namespace〉: 〈1;patch〉
          })
        }
        #JSONOp: ({
          op: "add"
          path: string
          value: _
        }|{
          op: "remove"
          path: string
        })
        #Main: {
          namespace: string
          output: 〈0;let jsonPatch#5〉.output
          let jsonPatch#5 = (〈1;#JSONPatch〉 & {
            let base#8 = (〈2;#Resource〉 & {})
            let withMsg#9 = (〈0;let base#8〉 & {
              someMsg: "foo"
            })
            namespace: 〈1;namespace〉
            patch: 〈0;let withMsg#9〉.patches
          })
        }
        out: (〈0;#Main〉 & {
          namespace: "ns1"
        }).output
      }
    }
  }
  issue3801: {
    nonRooted: {
      full: {
        #Output: {
          namespace: string
          output: (〈1;#SubOutput〉 & {
            namespace: 〈1;namespace〉
            let base2#A = {
              let base#B = (〈3;#Base〉 & {})
              output: (〈0;let base#B〉 & {
                val0: "foo"
              })
            }
            patch: 〈0;let base2#A〉.output.value
          }).output
        }
        out: (〈0;#Output〉 & {
          namespace: "ns1"
        }).output
        #SubOutput: {
          namespace?: string
          patch: _
          output: {
            〈1;namespace〉: 〈1;patch〉
          }
        }
        #Base: {
          val0: string
          let msgObj#D = [
            〈1;val0〉,
          ][0]
          value: 〈0;let msgObj#D〉
        }
      }
    }
  }
}
