-- in.cue --
a: close({
	a: b: int
})

b: a & {x: int} @test(err, code=eval, at=x, contains="field not allowed", pos=[0:9]) // err
c: a & {a: c: int} @test(eq, {a: {c: int, b: int}}) // okay (non-recursive close)

inDisjunctions: {
	x: [string]: #Def
	#Def: [string]: {
		a: b: true
		let X = a
		close({uint: a: b: X.b}) | close({string: a: b: true})
	}
	x: socket: string: {}
	x: syslog: x.socket
	x: syslog: xxx: {}

	@test(eq, {
		x: {
			socket: string: {
				a: b: true
				uint: a: b: true
			} | {
				a: b: true
				string: a: b: true
			}
			syslog: {
				string: {
					a: b: true
					uint: a: b: true
				} | {
					a: b: true
					string: a: b: true
				}
				xxx: {
					a: b: true
					uint: a: b: true
				} | {
					a: b: true
					string: a: b: true
				}
			}
		}
		#Def: {}
	})
}

// issue642: Tests that close() with merged struct patterns (concrete fields and
// bulk optional) correctly allows subfields. https://cuelang.org/issue/642
issue642: {
	test: close({
		a: _
		b: x: _
	} & {
		[string]: y: _
	}) @test(closed)

	test: a: x: _
	test: b: x: _

	@test(eq, {
		test: {
			[string]: y: _
			a: {
				y: _
				x: _
			}
			b: {
				x: _
				y: _
			}
		}
	})
}

// Issue 642
withSubfields: {
	test: close({
		a: _
		b: x:        _
		[string]: y: _
	})

	test: a: x: _
	test: b: x: _

	@test(eq, {
		test: {
			[string]: y: _
			a: {
				y: _
				x: _
			}
			b: {
				x: _
				y: _
			}
		}
	})
}

// issue3572: Tests that close({...}) allows additional fields when unified
// with an open struct. https://cuelang.org/issue/3572
issue3572: {
  t1: close({...}) & {a: 5} @test(eq, {a: 5}) @test(closed)

  e: close({...}) @test(closed) // TODO: @test(allows=expr)

  t2: e & {a: 5} @test(eq, {a: 5}) @test(closed)
}

// issue3580: Tests that close() does not panic when a field inside the closed
// struct references another field of the same struct. https://cuelang.org/issue/3580
issue3580: {
	x: close({
		a: _
		b: x.a
	}) @test(closed)
}
-- embed.cue --
nestedEmbedded: base: {
	close({ foo?: close({ foo?: _ }) })
	foo: allowed: 5

	@test(eq, { foo: { allowed: 5, foo?: _ } })
}
nestedEmbedded: t1: {
	{ close({ foo?: close({ foo?: _ }) }) }
	foo: allowed: 5

	@test(eq, { foo: { allowed: 5, foo?: _ } })
}
nestedEmbedded: t2: {
	{{ close({ foo?: close({ foo?: _ }) }) }}
	foo: allowed: 5

	@test(eq, { foo: { allowed: 5, foo?: _ } })
}
unified: base: {
	x: close({ foo?: close({ foo?: _ }) })
	x: foo: disallowed: 5 @test(err, code=eval, contains="field not allowed", pos=[0:10])
}
unified: t1: {
	x: { close({ foo?: close({ foo?: _ }) }) }
	x: foo: disallowed: 5 @test(err, code=eval, contains="field not allowed", pos=[0:10])
}
unified: t2: {
	x: {{ close({ foo?: close({ foo?: _ }) }) }}
	x: foo: disallowed: 5 @test(err, code=eval, contains="field not allowed", pos=[0:10])
}
nestedWithSelfReference: t1: {
	_schema: close({
		foo?: _schema
	}) @test(shareID=S)
	// TODO(inline): make work for hidden fields.
	// @test(err, at=_schema.foo, code=structural_cycle)
	// @test(shareID=S, at=x.foo.foo)

	x: {_schema}
	x: foo: disallowed: 5 @test(err, code=eval, contains="field not allowed", pos=[0:10])
}
nestedWithSelfReference: t2: {
	_schema: close({
		foo?: _schema
	})
	// TODO(inline): make work for hidden fields.
	// @test(err, at=foo, code=structural_cycle)
	x: {{_schema}}
	x: foo: disallowed: 5 @test(err, code=eval, contains="field not allowed", pos=[0:10])
}
-- out/eval/stats --
Leaks:  0
Freed:  273
Reused: 234
Allocs: 39
Retain: 0

Unifications: 183
Conjuncts:    298
Disjuncts:    6

NumCloseIDs: 88

ConjunctInfos:       239
MaxConjunctInfos:    5
MaxReqSets:          6
MaxRedirect:         2
-- out/errors.txt --
[eval] unified.base.x.foo.disallowed: field not allowed:
    ./embed.cue:21:10
[eval] unified.t1.x.foo.disallowed: field not allowed:
    ./embed.cue:25:10
[eval] unified.t2.x.foo.disallowed: field not allowed:
    ./embed.cue:29:10
[structural_cycle] nestedWithSelfReference.t1._schema.foo: structural cycle
[eval] nestedWithSelfReference.t1.x.foo.disallowed: field not allowed:
    ./embed.cue:40:10
[structural_cycle] nestedWithSelfReference.t2._schema.foo: structural cycle
[eval] nestedWithSelfReference.t2.x.foo.disallowed: field not allowed:
    ./embed.cue:49:10
[eval] b.x: field not allowed:
    ./in.cue:5:9
-- out/diff-v2-v3.txt --
Reordering
Let differs.
Verdict: cosmetic -- Field reordering; let binding differences. No semantic change.
