# Tests various cycle patterns involving constraint propagation through pattern
# constraints, self-triggering cycles, and mutually triggering cycles.
-- in.cue --
// Not a cycle: the structure in x (c) only triggers one further
// instantiation of y, but not enough to trigger another round.
safeCycle: simple2: {
	y: [string]: b: c: y
	x: y
	x: c: y
}

// Not a cycle: the structure in x (c) only triggers one further
// instantiation of y, but not enough to trigger another round.
safeCycle: long1: {
	y: [string]: b: y
	x: y
	x: c: b: e: b: c: _
}

// Not a cycle: the structure in x (c) only triggers one further
// instantiation of y, but not enough to trigger another round.
safeCycle: long2: {
	a: [string]: b: a
	x: a
	x: c: b: c: b: c: {}
}

// noCyclePattern is similar to the above cases, but involving more patterns
// and various lengths.
noCyclePattern: _

noCyclePattern: t1: {
	#D: [string]: #D
	a: {}
	[string]: #D
	[string]: b: c: {}
}

noCyclePattern: t2: {
	#D: [string]: x: #D
	a: {}
	[string]: #D
	[string]: b: x: c: {}
}

noCyclePattern: t3: {
	_D: [string]: _T
	_T: x:        _D
	[string]: _D
	[string]: b: x: c: {}
	a: {}
}

noCyclePattern: t4: {
	_D: [string]: x: _T
	_T: y: _D
	[string]: _D
	[string]: b: x: {}
	a: {}
}

noCyclePattern: t5: {
	_D: [string]: x: _T
	_T: y: _D
	[string]: _D
	[string]: b: x: y: c: x: {}
	a: {}
}

// This example also has an embedding, in which case it should still behave
// the same.
noCyclePattern: t6: {
	#D: [string]: #E

	#E: t: #T

	#T: {
		{object: #S}
	}

	#S: y:       #D
	[string]: x: #D
	[string]: x: r: t: object: y: foo: t: object: {}
	bar: {}
}

// Cycle: x cyclic. The pattern constraint should not be seen as
// "adding new structure", thereby permitting the cycle.
noCancelSelfInvoke: t1: {
	y: [string]: b: y
	x: y
	x: c: x
}

// Even though these cycles cross pattern boundaries, they are still structural
// cycles as the reference includes a field that triggers the pattern.
selfTriggerCycle: _

// TODO: This does not show an explicit cycle within `a`, only a "child" cycle.
// However, upon investigation this error is present in the tree. Either way,
// the error only needs to be reported once, so this is not a huge problem.
selfTriggerCycle: t1: {
	a: #T
	#T: {
		[string]: #T
		b: {}
	}
}

selfTriggerCycle: t2: {
	#T: [string]: X={
		a: X
	}
	b: #T
	b: c: {}
}

selfTriggerCycle: long1: {
	// The long string of nested fields will initially exempt the structural
	// cycle, but they will eventually run out, causing the cycle to be
	// triggered.
	a: [string]: b: a // -> record conjunct from pattern per field.
	a: c: b: c: b: c: {} // -> track if any of the fields is not a cycle.
}

// TODO: see comment at selfTriggerCycle.t1
selfTriggerCycle: issue1503: {
	a: #T & {
		a: one: link: a: two: {}
	}
	#T: {
		a: [string]: link: #T
		a: b: {}
	}
}

mutuallyTriggeringCycle: t1: {
	// Even though y itself, as well as each of the x fields are not cyclic,
	// the combination of the two x conjuncts are, as the b fields of y will
	// trigger the pattern constraints in an interleaved fashion.
	y: [string]: b: y
	x: y
	x: c: y
}
-- ring.cue --
brokenRing: t1: {
	// NOTE that two permutations here have different results. That is less
	// of an issue than it seems, as the structural cycle is hidden behind
	// an optional path, which we generally allow.
	// TODO: it would be nicer, though, if the representation were consistent.
	p1: {
		D: a?: T
		T: b:  D
	}
	p2: {
		// TODO(eval): fix permute or eval: error messages differ based on path in the two cases.
		// @test(permute, count=2)
		T: b:  D
		D: a?: T
	}
}

brokenRing: t2: p1: {
	D: [string]: T
	T: b:        D
}

brokenRing: t2: p2: {
	T: b:        D
	D: [string]: T
}

cyclicRing: t1: {
	D: a: T
	T: b: D
}
-- issue3476.cue --
// `cue def -e '#a'` causes a stack overflow. The definition #a selects a
// field from a conjunction `(#b & {x: _}).x` where #b contains a
// self-referential pattern `#c: [string]: #c`. This combination causes the
// exporter to recurse infinitely instead of detecting the cycle.
// https://cuelang.org/issue/3476
issue3476: {
	#a: (#b & {x: _}).x
	#b: {
		x?: #c
		#c: [string]: #c
	}
}
-- issue3509.cue --
// evalv3 causes infinite work and OOM panic where evalv2 is fast. A
// self-referential list constraint `#matrixConfig: [...#matrixConfig] | string`
// combined with indirect field access `#job: (#Workflow & {job: _}).job` causes
// the new evaluator to loop indefinitely.
// https://cuelang.org/issue/3509
issue3509: {
	out: #job.step & "foo"

	#job: (#Workflow & {job: _}).job

	#Workflow: {
		job: step: string

		#matrixConfig: [...#matrixConfig] | string
		matrix?: [string]: [...#matrixConfig]
	}
}
-- out/compile --
--- in.cue
{
  safeCycle: {
    simple2: {
      y: {
        [string]: {
          b: {
            c: 〈3;y〉
          }
        }
      }
      x: 〈0;y〉
      x: {
        c: 〈1;y〉
      }
    }
  }
  safeCycle: {
    long1: {
      y: {
        [string]: {
          b: 〈2;y〉
        }
      }
      x: 〈0;y〉
      x: {
        c: {
          b: {
            e: {
              b: {
                c: _
              }
            }
          }
        }
      }
    }
  }
  safeCycle: {
    long2: {
      a: {
        [string]: {
          b: 〈2;a〉
        }
      }
      x: 〈0;a〉
      x: {
        c: {
          b: {
            c: {
              b: {
                c: {}
              }
            }
          }
        }
      }
    }
  }
  noCyclePattern: _
  noCyclePattern: {
    t1: {
      #D: {
        [string]: 〈1;#D〉
      }
      a: {}
      [string]: 〈0;#D〉
      [string]: {
        b: {
          c: {}
        }
      }
    }
  }
  noCyclePattern: {
    t2: {
      #D: {
        [string]: {
          x: 〈2;#D〉
        }
      }
      a: {}
      [string]: 〈0;#D〉
      [string]: {
        b: {
          x: {
            c: {}
          }
        }
      }
    }
  }
  noCyclePattern: {
    t3: {
      _D: {
        [string]: 〈1;_T〉
      }
      _T: {
        x: 〈1;_D〉
      }
      [string]: 〈0;_D〉
      [string]: {
        b: {
          x: {
            c: {}
          }
        }
      }
      a: {}
    }
  }
  noCyclePattern: {
    t4: {
      _D: {
        [string]: {
          x: 〈2;_T〉
        }
      }
      _T: {
        y: 〈1;_D〉
      }
      [string]: 〈0;_D〉
      [string]: {
        b: {
          x: {}
        }
      }
      a: {}
    }
  }
  noCyclePattern: {
    t5: {
      _D: {
        [string]: {
          x: 〈2;_T〉
        }
      }
      _T: {
        y: 〈1;_D〉
      }
      [string]: 〈0;_D〉
      [string]: {
        b: {
          x: {
            y: {
              c: {
                x: {}
              }
            }
          }
        }
      }
      a: {}
    }
  }
  noCyclePattern: {
    t6: {
      #D: {
        [string]: 〈1;#E〉
      }
      #E: {
        t: 〈1;#T〉
      }
      #T: {
        {
          object: 〈2;#S〉
        }
      }
      #S: {
        y: 〈1;#D〉
      }
      [string]: {
        x: 〈1;#D〉
      }
      [string]: {
        x: {
          r: {
            t: {
              object: {
                y: {
                  foo: {
                    t: {
                      object: {}
                    }
                  }
                }
              }
            }
          }
        }
      }
      bar: {}
    }
  }
  noCancelSelfInvoke: {
    t1: {
      y: {
        [string]: {
          b: 〈2;y〉
        }
      }
      x: 〈0;y〉
      x: {
        c: 〈1;x〉
      }
    }
  }
  selfTriggerCycle: _
  selfTriggerCycle: {
    t1: {
      a: 〈0;#T〉
      #T: {
        [string]: 〈1;#T〉
        b: {}
      }
    }
  }
  selfTriggerCycle: {
    t2: {
      #T: {
        [string]: {
          a: 〈1〉
        }
      }
      b: 〈0;#T〉
      b: {
        c: {}
      }
    }
  }
  selfTriggerCycle: {
    long1: {
      a: {
        [string]: {
          b: 〈2;a〉
        }
      }
      a: {
        c: {
          b: {
            c: {
              b: {
                c: {}
              }
            }
          }
        }
      }
    }
  }
  selfTriggerCycle: {
    issue1503: {
      a: (〈0;#T〉 & {
        a: {
          one: {
            link: {
              a: {
                two: {}
              }
            }
          }
        }
      })
      #T: {
        a: {
          [string]: {
            link: 〈3;#T〉
          }
        }
        a: {
          b: {}
        }
      }
    }
  }
  mutuallyTriggeringCycle: {
    t1: {
      y: {
        [string]: {
          b: 〈2;y〉
        }
      }
      x: 〈0;y〉
      x: {
        c: 〈1;y〉
      }
    }
  }
}
--- issue3476.cue
{
  issue3476: {
    #a: (〈0;#b〉 & {
      x: _
    }).x
    #b: {
      x?: 〈0;#c〉
      #c: {
        [string]: 〈1;#c〉
      }
    }
  }
}
--- issue3509.cue
{
  issue3509: {
    out: (〈0;#job〉.step & "foo")
    #job: (〈0;#Workflow〉 & {
      job: _
    }).job
    #Workflow: {
      job: {
        step: string
      }
      #matrixConfig: ([
        ...〈1;#matrixConfig〉,
      ]|string)
      matrix?: {
        [string]: [
          ...〈2;#matrixConfig〉,
        ]
      }
    }
  }
}
--- ring.cue
{
  brokenRing: {
    t1: {
      p1: {
        D: {
          a?: 〈1;T〉
        }
        T: {
          b: 〈1;D〉
        }
      }
      p2: {
        T: {
          b: 〈1;D〉
        }
        D: {
          a?: 〈1;T〉
        }
      }
    }
  }
  brokenRing: {
    t2: {
      p1: {
        D: {
          [string]: 〈1;T〉
        }
        T: {
          b: 〈1;D〉
        }
      }
    }
  }
  brokenRing: {
    t2: {
      p2: {
        T: {
          b: 〈1;D〉
        }
        D: {
          [string]: 〈1;T〉
        }
      }
    }
  }
  cyclicRing: {
    t1: {
      D: {
        a: 〈1;T〉
      }
      T: {
        b: 〈1;D〉
      }
    }
  }
}
