# This test is important in locking in the closedness behavior
# of evalv2, where inline structs cause some closedness errors
# to be ignored. evalv3 mimics this behavior for the sake of
# backwards compatibility and not breaking existing users.

-- cue.mod/module.cue --
module: "mod.test"
language: version: "v0.9.0"
-- in.cue --
import (
	pkg "foo.com/example"
)

// This test tests that closeness errors are eliminated that occur within an
// inline struct.
inlineErr: {
	#D: {a: 1}

	// Allow a closedness error that occurs within an inline struct.
	def: x: (#D & {b: 2}).b
}

// These tests test that selecting into an inlined struct recursively opens this
// struct in OpenInline.
outerErr: {
	_inToOut: {
		in: _
		out: in.foo
	}

	// The closedness of #Inner should be opened in OpenInline mode.
	issue3534: {
		#Inner: foo: minor: 2
		#Outer: version: { major: 1, ... }

		t1: #Outer
		t1: version: (_inToOut & {in: #Inner}).out
	}

	// Test that the same principle works with the close builtin.
	usingClose: {
		// Same as above, but with an additional level of nesting.
		#Inner:  foo: close({minor: 2})
		#Outer: version: { major: 1, ... }

		t1: #Outer
		t1: version: (_inToOut & {in: #Inner}).out
	}

	// Test that the mechanism still works with extra nesting: the closedness of
	// #Inner should be opened recursively in OpenInline mode.
	extraNesting: {
		// Same as above, but with an additional level of nesting.
		#Inner: foo: x: minor: 2
		#Outer: version: x: { major: 1, ... }

		t1: #Outer
		t1: version: (_inToOut & {in: #Inner}).out
	}
}

// Here OpenInline does _not_ erase the error, as the error is already present
// in the referred definitions.

// faulty struct referenced from package
refFromPkg: (pkg.#E).b

refFromLet: {
	let X = {
		#D: a: 2
		#E: #D & {b: 1}
	}
	x: (X.#E).b
}

-- cue.mod/pkg/foo.com/example/example.cue --
package example

#D: a: 1
#E: #D & {b: 1}

-- out/eval/stats --
Leaks:  18
Freed:  73
Reused: 69
Allocs: 22
Retain: 37

Unifications: 91
Conjuncts:    141
Disjuncts:    107
-- out/evalalpha --
Errors:
outerErr.usingClose.t1.version.major: field not allowed:
    ./in.cue:35:22

Result:
(_|_){
  // [eval]
  inlineErr: (struct){
    #D: (#struct){
      a: (int){ 1 }
    }
    def: (struct){
      x: (int){ 2 }
    }
  }
  outerErr: (_|_){
    // [eval]
    _inToOut: (struct){
      in: (_){ _ }
      out: (_|_){
        // [incomplete] outerErr._inToOut.out: in.foo undefined as in is incomplete (type _):
        //     ./in.cue:19:8
      }
    }
    issue3534: (struct){
      #Inner: (#struct){
        foo: (#struct){
          minor: (int){ 2 }
        }
      }
      #Outer: (#struct){
        version: (#struct){
          major: (int){ 1 }
        }
      }
      t1: (#struct){
        version: (#struct){
          major: (int){ 1 }
          minor: (int){ 2 }
        }
      }
    }
    usingClose: (_|_){
      // [eval]
      #Inner: (#struct){
        foo: (#struct){
          minor: (int){ 2 }
        }
      }
      #Outer: (#struct){
        version: (#struct){
          major: (int){ 1 }
        }
      }
      t1: (_|_){
        // [eval]
        version: (_|_){
          // [eval]
          major: (_|_){
            // [eval] outerErr.usingClose.t1.version.major: field not allowed:
            //     ./in.cue:35:22
          }
          minor: (int){ 2 }
        }
      }
    }
    extraNesting: (struct){
      #Inner: (#struct){
        foo: (#struct){
          x: (#struct){
            minor: (int){ 2 }
          }
        }
      }
      #Outer: (#struct){
        version: (#struct){
          x: (#struct){
            major: (int){ 1 }
          }
        }
      }
      t1: (#struct){
        version: (#struct){
          x: (#struct){
            major: (int){ 1 }
            minor: (int){ 2 }
          }
        }
      }
    }
  }
  refFromPkg: (int){ 1 }
  refFromLet: (struct){
    let X#1 = (_|_){
      // [eval]
      #D: (#struct){
        a: (int){ 2 }
      }
      #E: (_|_){
        // [eval]
        b: (_|_){
          // [eval] refFromLet.X.#E.b: field not allowed:
          //     ./in.cue:62:13
        }
        a: (int){ 2 }
      }
    }
    x: (int){ 1 }
  }
}
-- diff/-out/evalalpha<==>+out/eval --
diff old new
--- old
+++ new
@@ -1,10 +1,6 @@
 Errors:
 outerErr.usingClose.t1.version.major: field not allowed:
-    ./in.cue:19:8
-    ./in.cue:34:23
     ./in.cue:35:22
-    ./in.cue:37:7
-    ./in.cue:38:16
 
 Result:
 (_|_){
@@ -62,11 +58,7 @@
           // [eval]
           major: (_|_){
             // [eval] outerErr.usingClose.t1.version.major: field not allowed:
-            //     ./in.cue:19:8
-            //     ./in.cue:34:23
             //     ./in.cue:35:22
-            //     ./in.cue:37:7
-            //     ./in.cue:38:16
           }
           minor: (int){ 2 }
         }
@@ -106,13 +98,11 @@
       }
       #E: (_|_){
         // [eval]
-        a: (int){ 2 }
         b: (_|_){
           // [eval] refFromLet.X.#E.b: field not allowed:
-          //     ./in.cue:61:7
-          //     ./in.cue:62:7
           //     ./in.cue:62:13
         }
+        a: (int){ 2 }
       }
     }
     x: (int){ 1 }
-- out/eval --
Errors:
outerErr.usingClose.t1.version.major: field not allowed:
    ./in.cue:19:8
    ./in.cue:34:23
    ./in.cue:35:22
    ./in.cue:37:7
    ./in.cue:38:16

Result:
(_|_){
  // [eval]
  inlineErr: (struct){
    #D: (#struct){
      a: (int){ 1 }
    }
    def: (struct){
      x: (int){ 2 }
    }
  }
  outerErr: (_|_){
    // [eval]
    _inToOut: (struct){
      in: (_){ _ }
      out: (_|_){
        // [incomplete] outerErr._inToOut.out: in.foo undefined as in is incomplete (type _):
        //     ./in.cue:19:8
      }
    }
    issue3534: (struct){
      #Inner: (#struct){
        foo: (#struct){
          minor: (int){ 2 }
        }
      }
      #Outer: (#struct){
        version: (#struct){
          major: (int){ 1 }
        }
      }
      t1: (#struct){
        version: (#struct){
          major: (int){ 1 }
          minor: (int){ 2 }
        }
      }
    }
    usingClose: (_|_){
      // [eval]
      #Inner: (#struct){
        foo: (#struct){
          minor: (int){ 2 }
        }
      }
      #Outer: (#struct){
        version: (#struct){
          major: (int){ 1 }
        }
      }
      t1: (_|_){
        // [eval]
        version: (_|_){
          // [eval]
          major: (_|_){
            // [eval] outerErr.usingClose.t1.version.major: field not allowed:
            //     ./in.cue:19:8
            //     ./in.cue:34:23
            //     ./in.cue:35:22
            //     ./in.cue:37:7
            //     ./in.cue:38:16
          }
          minor: (int){ 2 }
        }
      }
    }
    extraNesting: (struct){
      #Inner: (#struct){
        foo: (#struct){
          x: (#struct){
            minor: (int){ 2 }
          }
        }
      }
      #Outer: (#struct){
        version: (#struct){
          x: (#struct){
            major: (int){ 1 }
          }
        }
      }
      t1: (#struct){
        version: (#struct){
          x: (#struct){
            major: (int){ 1 }
            minor: (int){ 2 }
          }
        }
      }
    }
  }
  refFromPkg: (int){ 1 }
  refFromLet: (struct){
    let X#1 = (_|_){
      // [eval]
      #D: (#struct){
        a: (int){ 2 }
      }
      #E: (_|_){
        // [eval]
        a: (int){ 2 }
        b: (_|_){
          // [eval] refFromLet.X.#E.b: field not allowed:
          //     ./in.cue:61:7
          //     ./in.cue:62:7
          //     ./in.cue:62:13
        }
      }
    }
    x: (int){ 1 }
  }
}
-- out/compile --
--- in.cue
{
  inlineErr: {
    #D: {
      a: 1
    }
    def: {
      x: (〈1;#D〉 & {
        b: 2
      }).b
    }
  }
  outerErr: {
    _inToOut: {
      in: _
      out: 〈0;in〉.foo
    }
    issue3534: {
      #Inner: {
        foo: {
          minor: 2
        }
      }
      #Outer: {
        version: {
          major: 1
          ...
        }
      }
      t1: 〈0;#Outer〉
      t1: {
        version: (〈2;_inToOut〉 & {
          in: 〈2;#Inner〉
        }).out
      }
    }
    usingClose: {
      #Inner: {
        foo: close({
          minor: 2
        })
      }
      #Outer: {
        version: {
          major: 1
          ...
        }
      }
      t1: 〈0;#Outer〉
      t1: {
        version: (〈2;_inToOut〉 & {
          in: 〈2;#Inner〉
        }).out
      }
    }
    extraNesting: {
      #Inner: {
        foo: {
          x: {
            minor: 2
          }
        }
      }
      #Outer: {
        version: {
          x: {
            major: 1
            ...
          }
        }
      }
      t1: 〈0;#Outer〉
      t1: {
        version: (〈2;_inToOut〉 & {
          in: 〈2;#Inner〉
        }).out
      }
    }
  }
  refFromPkg: 〈import;"foo.com/example"〉.#E.b
  refFromLet: {
    let X#1 = {
      #D: {
        a: 2
      }
      #E: (〈0;#D〉 & {
        b: 1
      })
    }
    x: 〈0;let X#1〉.#E.b
  }
}
