// Tests struct embedding behavior including comprehension-based embeddings and
// closedness enforcement. Verifies that "field not allowed" errors are correctly
// raised when embedding adds fields disallowed by a closed definition, and that
// closedness inversion with open fields works correctly.
-- in.cue --
#A: {
	Common
	...
}

Common: {
	a: int
	b: int
}

x: #A
x: {
	c: int
}

#B: {
	if true {
		a: int
	}
	...
}

y: #B
y: {
	c: int
}
-- noclose.cue --
double: {
	a: b?: c: d: "c"
	a: {{
		b: {{
			c: e: "member"
		}}
	}}
}
doubleAsComprehension: {
	a: b?: c: d: "c"
	a: {
		if true {
			b: {
				if true {
					c: e: "member"
				}
			}
		}
	}
}
fieldNotAllowed: {
	// This was not correctly enforced in evalv2.
	t1: {
		#X: {
			objs: {
				extra: unused: "foo"
				...
			}
			...
		}
		#Y: unused: {}
		addTwo: #X & {
			#Y
			objs: name: "Two"
		}
	}
	t2: {
		#AddObjsExtra: {
			objs: [string]: {
				extra: unused: "foo"
				...
			}
			...
		}
		#EmbedUnused: unused: {}
		#AddTwo: #AddObjsExtra & {
			#EmbedUnused
			objs: two: {
				name: "Two"
				labels: app: objs.one.name
			}
		}
		out: #AddTwo & {
			objs: one: {
				name: "One"
			}
		}
	}
	t3: {
		x: #X
		#X: y: {
			z: {
				a: [string]: string
				b: E:        "1"
			}
			...
		}
		x1: y:     #Y
		#Y: port?: string

		x: y: {
			z: b: E: string
			port: z.b.E
			z: a: c?: string
		}

		if true {
			w: x & {
				y: x1.y
			}
		}
	}
}
closednessInversion: {
	base: #X
	base: x: #Y
	#X: x: {...}
	#Y: y: string | *"default"
	out: {
		base.x
		a: b: c: "d"
	}
}
-- out/compile --
--- in.cue
{
  #A: {
    〈1;Common〉
    ...
  }
  Common: {
    a: int
    b: int
  }
  x: 〈0;#A〉
  x: {
    c: int
  }
  #B: {
    if true {
      a: int
    }
    ...
  }
  y: 〈0;#B〉
  y: {
    c: int
  }
}
--- noclose.cue
{
  double: {
    a: {
      b?: {
        c: {
          d: "c"
        }
      }
    }
    a: {
      {
        b: {
          {
            c: {
              e: "member"
            }
          }
        }
      }
    }
  }
  doubleAsComprehension: {
    a: {
      b?: {
        c: {
          d: "c"
        }
      }
    }
    a: {
      if true {
        b: {
          if true {
            c: {
              e: "member"
            }
          }
        }
      }
    }
  }
  fieldNotAllowed: {
    t1: {
      #X: {
        objs: {
          extra: {
            unused: "foo"
          }
          ...
        }
        ...
      }
      #Y: {
        unused: {}
      }
      addTwo: (〈0;#X〉 & {
        〈1;#Y〉
        objs: {
          name: "Two"
        }
      })
    }
    t2: {
      #AddObjsExtra: {
        objs: {
          [string]: {
            extra: {
              unused: "foo"
            }
            ...
          }
        }
        ...
      }
      #EmbedUnused: {
        unused: {}
      }
      #AddTwo: (〈0;#AddObjsExtra〉 & {
        〈1;#EmbedUnused〉
        objs: {
          two: {
            name: "Two"
            labels: {
              app: 〈3;objs〉.one.name
            }
          }
        }
      })
      out: (〈0;#AddTwo〉 & {
        objs: {
          one: {
            name: "One"
          }
        }
      })
    }
    t3: {
      x: 〈0;#X〉
      #X: {
        y: {
          z: {
            a: {
              [string]: string
            }
            b: {
              E: "1"
            }
          }
          ...
        }
      }
      x1: {
        y: 〈1;#Y〉
      }
      #Y: {
        port?: string
      }
      x: {
        y: {
          z: {
            b: {
              E: string
            }
          }
          port: 〈0;z〉.b.E
          z: {
            a: {
              c?: string
            }
          }
        }
      }
      if true {
        w: (〈1;x〉 & {
          y: 〈2;x1〉.y
        })
      }
    }
  }
  closednessInversion: {
    base: 〈0;#X〉
    base: {
      x: 〈1;#Y〉
    }
    #X: {
      x: {
        ...
      }
    }
    #Y: {
      y: (string|*"default")
    }
    out: {
      〈1;base〉.x
      a: {
        b: {
          c: "d"
        }
      }
    }
  }
}
