///|
/// Cache of `(declarations -> aliases)` keyed by physical identity of
/// the declarations array. `eval_type_alias_bindings` is on the
/// reflect / type-cast / constraint hot path — `pkspec/Test.pkl`'s
/// sample profile attributed 182 of 5000 samples to this single
/// function and another 119 to the `Array.push` inside it. Within a
/// single `eval_source` call every caller passes the same
/// `program.declarations` reference, so a small physical-equality
/// cache hits ~100%.
let type_alias_bindings_cache : Ref[
  Array[(Array[Declaration], Array[EvalTypeAliasBinding])],
] = { val: [] }

///|
fn eval_type_alias_bindings(
  declarations : Array[Declaration],
) -> Array[EvalTypeAliasBinding] {
  let cache = type_alias_bindings_cache.val
  for entry in cache {
    let (cached_decls, cached_aliases) = entry
    if physical_equal(cached_decls, declarations) {
      return cached_aliases
    }
  }
  let aliases = build_type_alias_bindings(declarations)
  // Keep the cache small: distinct declaration arrays are
  // proportional to the number of imported modules in a session, and
  // a 4-entry LRU comfortably covers single-CLI eval + bench loops.
  while type_alias_bindings_cache.val.length() >= 4 {
    let _ = type_alias_bindings_cache.val.remove(0)
  }
  type_alias_bindings_cache.val.push((declarations, aliases))
  aliases
}

///|
fn build_type_alias_bindings(
  declarations : Array[Declaration],
) -> Array[EvalTypeAliasBinding] {
  let aliases : Array[EvalTypeAliasBinding] = []
  // PKL-148bb: Apple Pkl's `pkl:base` ships fixed-width integer
  // typealiases (`UInt8` = `Int(isBetween(0, 255))`, etc.). Seed them
  // so user code can annotate `x: UInt8 = 255` without explicitly
  // importing — the constraint cascade then enforces the range.
  // `api/typeAliases` and `basic/int` both rely on these.
  aliases.push({ name: "NonNull", target: "Any(!(this is Null))" })
  aliases.push({ name: "UInt", target: "Int(isPositive)" })
  aliases.push({ name: "UInt8", target: "Int(isBetween(0, 255))" })
  aliases.push({ name: "UInt16", target: "Int(isBetween(0, 65535))" })
  aliases.push({ name: "UInt32", target: "Int(isBetween(0, 4294967295))" })
  aliases.push({ name: "Int8", target: "Int(isBetween(-128, 127))" })
  aliases.push({ name: "Int16", target: "Int(isBetween(-32768, 32767))" })
  aliases.push({
    name: "Int32",
    target: "Int(isBetween(-2147483648, 2147483647))",
  })
  aliases.push({ name: "Uri", target: "String" })
  for declaration in declarations {
    match declaration {
      TypeAliasDeclaration(type_alias) =>
        aliases.push({ name: type_alias.name, target: type_alias.target })
      ClassDeclaration(_) | FunctionDeclaration(_) => ()
    }
  }
  aliases
}

///|
fn eval_constrained_type_source_name_with_depth(
  name : String,
  aliases : Array[EvalTypeAliasBinding],
  depth : Int,
) -> String? {
  if depth > 8 {
    return None
  }
  if pkl_constrained_type_annotation_has_supported_constraint(name) {
    return Some(name)
  }
  match lookup_eval_type_alias(aliases, name) {
    Some(target) =>
      eval_constrained_type_source_name_with_depth(target, aliases, depth + 1)
    None => None
  }
}

///|
fn eval_resolved_type_alias_with_depth(
  name : String,
  aliases : Array[EvalTypeAliasBinding],
  depth : Int,
) -> String {
  if depth > 8 {
    return name
  }
  match lookup_eval_type_alias(aliases, name) {
    Some(target) =>
      eval_resolved_type_alias_with_depth(target, aliases, depth + 1)
    None => name
  }
}

///|
/// Cache of resolved type-alias chains keyed by physical identity of
/// the `aliases` array (stable per-eval thanks to
/// `type_alias_bindings_cache`) plus the input `name`. The walk
/// itself bounces through `lookup_eval_type_alias.get` which is now
/// Map-backed but still costs a hash per step; on
/// `apple-pkl/stdlib/base.pkl` this function is called thousands of
/// times for the same names during the constraint-validation pass.
priv struct ResolvedAliasEntry {
  aliases : Array[EvalTypeAliasBinding]
  memo : Map[String, String]
}

///|
let resolved_alias_cache : Ref[Array[ResolvedAliasEntry]] = { val: [] }

///|
fn eval_resolved_type_alias(
  name : String,
  aliases : Array[EvalTypeAliasBinding],
) -> String {
  let cache = resolved_alias_cache.val
  let mut entry_opt : ResolvedAliasEntry? = None
  for entry in cache {
    if physical_equal(entry.aliases, aliases) {
      entry_opt = Some(entry)
      break
    }
  }
  let entry = match entry_opt {
    Some(e) => e
    None => {
      let fresh : ResolvedAliasEntry = { aliases, memo: Map([], capacity=32) }
      while resolved_alias_cache.val.length() >= 4 {
        let _ = resolved_alias_cache.val.remove(0)
      }
      resolved_alias_cache.val.push(fresh)
      fresh
    }
  }
  match entry.memo.get(name) {
    Some(cached) => return cached
    None => ()
  }
  let result = eval_resolved_type_alias_with_depth(name, aliases, 0)
  entry.memo[name] = result
  result
}

///|
fn eval_constrained_type_source_name(
  name : String,
  aliases : Array[EvalTypeAliasBinding],
) -> String? {
  eval_constrained_type_source_name_with_depth(name, aliases, 0)
}

///|
// PKL-138: coerce an empty `ObjectValue([])` to the empty Listing /
// Mapping value when the binding's type annotation requires it. Parsers
// can't tell whether `new {}` (no explicit type, empty body) is meant
// to be a Listing, Mapping, or Object — the binding's type annotation
// is the disambiguator, applied here at eval time.
//
// Non-empty `ObjectValue`s aren't coerced: if the body had real entries
// they would have been parsed as listing / mapping body via the
// `parse_inferred_new_body` peek (which dispatches on the first
// significant token), so a non-empty ObjectValue with a Listing /
// Mapping annotation is a real type mismatch.

///|
/// PKL-148: when a typed binding (`p: Person = new {}`) gets filled in
/// with an empty object, fall back to the class's declared default
/// property values. Without this the rendered output is `p {}` instead
/// of the inherited defaults. The expansion runs only for ObjectValue
/// with zero visible members where `type_name` names a user-defined
/// class; everything else passes through.
fn apply_class_defaults_for_type(
  value : Value,
  type_name : String?,
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
  resolve_import : (String) -> EvalResult?,
) -> Value {
  apply_class_defaults_for_type_seen(
    value,
    type_name,
    bindings,
    env,
    class_env,
    cache,
    declarations,
    diagnostics,
    resolve_import,
    [],
  )
}

///|
/// `_seen` variant carrying the in-progress class-name chain so a
/// property typed as a class higher in the chain (`class A { b: B }`,
/// `class B { a: A }`) returns the empty-default shape instead of
/// recursing into `synthesize_default_for_type` again and overflowing
/// the call stack. `seen` is copied into a private array at the
/// boundary so sibling property synthesis remains independent — two
/// `bar: Bar` properties at the same class layer must both expand
/// `Bar` once each, not skip the second.
fn apply_class_defaults_for_type_seen(
  value : Value,
  type_name : String?,
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
  resolve_import : (String) -> EvalResult?,
  seen : Array[String],
) -> Value {
  match (value, type_name) {
    (ObjectValue(members), Some(name)) =>
      if visible_members(members).length() != 0 {
        value
      } else {
        let base = name
        // Cycle guard is owned by `synthesize_default_for_type_seen`
        // (which is the only caller threading `seen` through). When the
        // wrapper is called from outside the class-default cycle path,
        // `seen` is empty.
        match lookup_class_binding(class_env, base) {
          Some(_) => {
            let seen_copy : Array[String] = []
            for s in seen {
              seen_copy.push(s)
            }
            let defaults = eval_class_default_members_seen(
              base,
              bindings,
              env,
              class_env,
              cache,
              [],
              declarations,
              diagnostics,
              resolve_import,
              seen_copy,
            )
            if defaults.length() == 0 {
              value
            } else {
              ObjectValue(merge_value_members(defaults, members))
            }
          }
          None => value
        }
      }
    _ => value
  }
}

///|
/// Synthesize a runtime default value for a typed property that has no
/// `=` initializer. Mirrors Apple Pkl's auto-default rules so a module
/// like `class P; p: P` renders as `p {}`. Returns `None` when the
/// type doesn't have a representable default (e.g., an unresolved
/// generic) so the caller can fall back to the abstract-slot skip.
fn synthesize_default_for_type(
  type_name : String?,
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
  resolve_import : (String) -> EvalResult?,
) -> Value? {
  synthesize_default_for_type_seen(
    type_name,
    bindings,
    env,
    class_env,
    cache,
    declarations,
    diagnostics,
    resolve_import,
    [],
  )
}

///|
/// `_seen` variant of `synthesize_default_for_type`. Threads the chain
/// of class names currently being synthesised so the
/// `synthesize → apply_class_defaults_for_type → eval_class_default_members`
/// path cannot reset the cycle guard and overflow the call stack on
/// mutually-recursive class types (the original failure observed on
/// `apple-pkl/stdlib/base.pkl`).
fn synthesize_default_for_type_seen(
  type_name : String?,
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
  resolve_import : (String) -> EvalResult?,
  seen : Array[String],
) -> Value? {
  match type_name {
    None => None
    Some(raw) => {
      let mut name = trim_spaces(raw)
      // Strip a wrapping `( ... )` introduced by nested union grouping
      // (`*("a"|*"b")|"c"`). Without unwrap the inner union split sees
      // the whole `("a"|*"b")` as one choice and treats it as an
      // unknown class name.
      while string_starts_with_char(name, '(') &&
            string_ends_with_char(name, ')') {
        let mut depth = 0
        let mut wraps_whole = true
        for i = 0; i < name.length(); i = i + 1 {
          let c = name[i].to_int().unsafe_to_char()
          if c == '(' {
            depth = depth + 1
          } else if c == ')' {
            depth = depth - 1
            if depth == 0 && i < name.length() - 1 {
              wraps_whole = false
              break
            }
          }
        }
        if wraps_whole && depth == 0 {
          name = trim_spaces(
            String::unsafe_substring(name, start=1, end=name.length() - 1),
          )
        } else {
          break
        }
      }
      match pkl_constrained_type_base_name(name) {
        Some(base) => name = trim_spaces(base)
        None => ()
      }
      // Nullable types (`T?`) default to null.
      if string_ends_with_char(name, '?') || name == "Null" || name == "Nothing" {
        return Some(NullValue)
      }
      // `A|B|*C|D` — any union choice carrying a leading `*` is the
      // default branch. Walk every top-level union choice (a single
      // string with no `|` falls through as a one-element array). Must
      // run BEFORE the string-literal arm so `"foo"|*"bar"` isn't
      // mistaken for one giant quoted literal.
      let choices = split_top_level_union_choices(name)
      if choices.length() > 1 {
        for choice in choices {
          let trimmed = trim_spaces(choice)
          if string_starts_with_char(trimmed, '*') {
            let starred = trim_spaces(
              String::unsafe_substring(trimmed, start=1, end=trimmed.length()),
            )
            return synthesize_default_for_type_seen(
              Some(starred),
              bindings,
              env,
              class_env,
              cache,
              declarations,
              diagnostics,
              resolve_import,
              seen,
            )
          }
        }
      }
      // String literal type `"foo"` defaults to the literal text.
      if name.length() >= 2 && name[0] == '"' && name[name.length() - 1] == '"' {
        let inner = String::unsafe_substring(
          name,
          start=1,
          end=name.length() - 1,
        )
        return Some(StringValue(inner))
      }
      if string_starts_with_char(name, '*') {
        let rest = String::unsafe_substring(name, start=1, end=name.length())
        return synthesize_default_for_type_seen(
          Some(trim_spaces(rest)),
          bindings,
          env,
          class_env,
          cache,
          declarations,
          diagnostics,
          resolve_import,
          seen,
        )
      }
      // Typealias dereference — `bar: Bar` where
      // `typealias Bar = "foo"|*"bar"` should pick up the starred
      // default through the resolved alias body, not the alias name.
      let aliases = eval_type_alias_bindings(declarations)
      let resolved = eval_resolved_type_alias(name, aliases)
      if resolved != name {
        return synthesize_default_for_type_seen(
          Some(resolved),
          bindings,
          env,
          class_env,
          cache,
          declarations,
          diagnostics,
          resolve_import,
          seen,
        )
      }
      // Structural collections fall back to their empty form. Set/Map
      // carry dedicated variants that round-trip through the PCF
      // constructor renderer; Listing/Mapping render as block bodies.
      // PKL-148j: `Collection` and `List` default to `ListValue`
      // (Apple Pkl renders `List()` for both, matching the
      // `basic/propertyDefaults` gold).
      //
      // Dispatch on the first character before running prefix checks —
      // every collection class name starts with one of L / M / S / C,
      // so non-collection names (the common case) bypass the
      // `has_prefix` boyer-moore scan entirely. Profiled accordingly.
      if name.length() > 0 {
        let first = name[0].to_int().unsafe_to_char()
        match first {
          'L' =>
            if name == "Listing" || name.has_prefix("Listing<") {
              return Some(ListingValue([]))
            } else if name == "List" || name.has_prefix("List<") {
              return Some(ListValue([]))
            }
          'M' =>
            if name == "Mapping" || name.has_prefix("Mapping<") {
              return Some(MappingValue([]))
            } else if name == "Map" || name.has_prefix("Map<") {
              return Some(MapValue([]))
            }
          'S' =>
            if name == "Set" || name.has_prefix("Set<") {
              return Some(SetValue([]))
            }
          'C' =>
            if name == "Collection" || name.has_prefix("Collection<") {
              return Some(ListValue([]))
            }
          _ => ()
        }
      }
      // User-defined class → `new T {}` with class defaults applied.
      let base = match string_index_of_char(name, '<') {
        idx if idx >= 0 => String::unsafe_substring(name, start=0, end=idx)
        _ => name
      }
      // PKL-148bc: skip re-entering a class we're already materialising.
      // The cycle path is property `bar: Bar` whose synthesis lands back
      // on `Bar` (mutually-recursive class types). Without this guard,
      // mutually-recursive types like `apple-pkl/stdlib/base.pkl` blow
      // the call stack instead of returning the empty-default shape.
      // The push into `seen` happens once inside
      // `eval_class_default_members_seen` so synthesize and apply just
      // forward the chain unchanged.
      if contains_string(seen, base) {
        return Some(ObjectValue([]))
      }
      match lookup_class_binding(class_env, base) {
        Some(_) =>
          Some(
            apply_class_defaults_for_type_seen(
              ObjectValue([]),
              Some(base),
              bindings,
              env,
              class_env,
              cache,
              declarations,
              diagnostics,
              resolve_import,
              seen,
            ),
          )
        None => None
      }
    }
  }
}

///|
fn coerce_value_to_annotated_type(value : Value, type_name : String?) -> Value {
  let head = match type_name {
    Some(name) => {
      let stripped = strip_lazy_collection_annotation_marker(name)
      let trimmed = pkl_strip_default_type_marker(pkl_constraint_trim(stripped))
      let mut base = match pkl_constrained_type_base_name(trimmed) {
        Some(base) => base
        None => trimmed
      }
      while base.has_suffix("?") {
        base = trim_spaces(
          String::unsafe_substring(base, start=0, end=base.length() - 1),
        )
      }
      match string_index_of_char(base, '<') {
        idx if idx >= 0 =>
          Some(String::unsafe_substring(base, start=0, end=idx))
        _ => Some(base)
      }
    }
    None => None
  }
  match (value, head) {
    (ObjectValue(members), Some("Listing" | "List")) if members.length() == 0 =>
      ListingValue([])
    (ObjectValue(members), Some("Mapping")) if members.length() == 0 =>
      MappingValue([])
    (ObjectValue(members), Some("Set")) if members.length() == 0 => SetValue([])
    (MappingValue(entries), Some("Dynamic"))
    | (DefaultedMappingValue(_, entries, _), Some("Dynamic")) =>
      dynamic_from_mapping_entries(entries)
    (ListingValue(elements), Some("Dynamic"))
    | (DefaultedListingValue(_, elements, _), Some("Dynamic"))
    | (ListValue(elements), Some("Dynamic"))
    | (SetValue(elements), Some("Dynamic")) => dynamic_from_elements(elements)
    _ => value
  }
}

///|
fn dynamic_from_mapping_entries(entries : Array[ValueEntry]) -> Value {
  let members : Array[ValueMember] = []
  for i = 0; i < entries.length(); i = i + 1 {
    members.push({
      name: "@subscript$\{i}",
      value: ObjectValue([
        { name: "@key", value: entries[i].key, source: None, annotations: [] },
        {
          name: "@value",
          value: entries[i].value,
          source: None,
          annotations: [],
        },
      ]),
      source: None,
      annotations: [],
    })
  }
  ObjectValue(tag_object_with_class(members, "Dynamic"))
}

///|

///|
/// PKL-153: returns `true` when `raw_type`'s base class is currently
/// being materialised on the class-default expansion stack. Used by
/// `apply_collection_default_for_type` to break the
/// `Listing = new {}` cycle. `raw_type` may carry generic args,
/// constraints, alias names, leading `*` (union default marker), or
/// trailing `?` (nullable); we strip all of those to land on the bare
/// class name that the materialising map keys on.
fn class_default_is_materializing(
  raw_type : String,
  declarations : Array[Declaration],
) -> Bool {
  let mut name = trim_spaces(raw_type)
  if name.length() == 0 {
    return false
  }
  if string_starts_with_char(name, '*') {
    name = trim_spaces(
      String::unsafe_substring(name, start=1, end=name.length()),
    )
  }
  if string_ends_with_char(name, '?') {
    name = trim_spaces(
      String::unsafe_substring(name, start=0, end=name.length() - 1),
    )
  }
  match pkl_constrained_type_base_name(name) {
    Some(base) => name = trim_spaces(base)
    None => ()
  }
  let aliases = eval_type_alias_bindings(declarations)
  let resolved = eval_resolved_type_alias(name, aliases)
  let base = match string_index_of_char(resolved, '<') {
    idx if idx >= 0 => String::unsafe_substring(resolved, start=0, end=idx)
    _ => resolved
  }
  let memo = class_default_memo_for(declarations)
  memo.materializing.get(base) is Some(true)
}

///|
fn apply_collection_default_for_type(
  value : Value,
  type_name : String?,
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  stack : Array[String],
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
  resolve_import : (String) -> EvalResult?,
) -> Value {
  let annotation = match type_name {
    Some(t) => t
    None => return value
  }
  let aliases = eval_type_alias_bindings(declarations)
  let resolved = eval_resolved_type_alias(annotation, aliases)
  let base = match pkl_constrained_type_base_name(resolved) {
    Some(b) => b
    None => resolved
  }
  match generic_argument_text(base, "Listing") {
    Some(element_type) =>
      match value {
        ListingValue(raw_elements) => {
          // PKL-153: skip element-default synthesis when the element type
          // names a class currently being materialised. The path lands
          // here from `eval_class_default_members_seen("Task")` evaluating
          // `deps: Listing = new {}`; synthesising Task's default
          // again would recurse forever (the `seen` array inside
          // `eval_class_default_members_seen` doesn't reach this far).
          // For an empty `raw_elements` the materialised default isn't
          // observable anyway — the rendered shape stays `Listing {}`.
          if class_default_is_materializing(element_type, declarations) {
            return value
          }
          match
            synthesize_default_for_type(
              Some(element_type),
              bindings,
              env,
              class_env,
              cache,
              declarations,
              diagnostics,
              resolve_import,
            ) {
            Some(default_value) => {
              if collection_default_value_is_empty(default_value) {
                return value
              }
              match
                materialize_listing_raw_elements(
                  raw_elements, default_value, bindings, env, class_env, cache, stack,
                  declarations, diagnostics, resolve_import,
                ) {
                Some(materialized) =>
                  return DefaultedListingValue(
                    raw_elements, materialized, default_value,
                  )
                None => return value
              }
            }
            None => return value
          }
        }
        _ => return value
      }
    None => ()
  }
  match generic_argument_text(base, "Mapping") {
    Some(inner_text) => {
      let parts = split_top_level_generic_arguments(inner_text)
      if parts.length() != 2 {
        return value
      }
      let value_type = parts[1]
      match value {
        MappingValue(raw_entries) => {
          if class_default_is_materializing(value_type, declarations) {
            return value
          }
          match
            synthesize_default_for_type(
              Some(value_type),
              bindings,
              env,
              class_env,
              cache,
              declarations,
              diagnostics,
              resolve_import,
            ) {
            Some(default_value) => {
              if collection_default_value_is_empty(default_value) {
                return value
              }
              match
                materialize_mapping_raw_entries(
                  raw_entries, default_value, bindings, env, class_env, cache, stack,
                  declarations, diagnostics, resolve_import,
                ) {
                Some(materialized) =>
                  return DefaultedMappingValue(
                    raw_entries, materialized, default_value,
                  )
                None => return value
              }
            }
            None => return value
          }
        }
        _ => return value
      }
    }
    None => ()
  }
  value
}

///|
fn collection_default_value_is_empty(value : Value) -> Bool {
  match value {
    ObjectValue(members) => visible_members(members).length() == 0
    _ => false
  }
}

///|
fn eval_constrained_type_annotation_value_is_valid(
  type_name : String?,
  value : Value,
  aliases : Array[EvalTypeAliasBinding],
  diagnostics : Array[Diagnostic],
) -> Bool {
  match type_name {
    Some(display_name) =>
      match eval_constrained_type_source_name(display_name, aliases) {
        Some(source_name) =>
          match
            pkl_constrained_type_annotation_value_rejection_message_from_source(
              display_name, source_name, value,
            ) {
            Some(message) => {
              diagnostics.push(diag(message))
              false
            }
            None => true
          }
        None => true
      }
    None => true
  }
}

///|
fn eval_user_defined_constrained_type_annotation_value_is_valid(
  type_name : String?,
  value : Value,
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
) -> Bool {
  match
    pkl_user_defined_constrained_type_annotation_value_rejection_message(
      type_name, value, declarations,
    ) {
    Some(message) => {
      diagnostics.push(diag(message))
      false
    }
    None => true
  }
}

///|
fn eval_lookup_class_decl(
  declarations : Array[Declaration],
  name : String,
) -> ClassDecl? {
  let mut found : ClassDecl? = None
  for declaration in declarations {
    match declaration {
      ClassDeclaration(class_decl) =>
        if class_decl.name == name {
          found = Some(class_decl)
        }
      FunctionDeclaration(_) | TypeAliasDeclaration(_) => ()
    }
  }
  found
}

///|
fn eval_class_property_annotation_with_depth(
  declarations : Array[Declaration],
  class_name : String,
  property_name : String,
  depth : Int,
) -> String? {
  if depth > 8 {
    return None
  }
  match eval_lookup_class_decl(declarations, class_name) {
    Some(class_decl) => {
      let mut found = false
      let mut annotation : String? = None
      // PKL-148bb: a class's `hidden` properties are stored under
      // `@hidden$` (parser-side prefixing). The caller passes the
      // bare member name (`f`, not `@hidden$f`), so accept either form
      // when matching.
      let hidden_prefixed = hidden_member_name(property_name)
      for property in class_decl.properties {
        if property.name == property_name || property.name == hidden_prefixed {
          found = true
          annotation = property.type_name
        }
      }
      // PKL-148i: a subclass property that omits the type annotation
      // (e.g. `name = "Pigeon"` while the parent declared
      // `name: String`) still inherits the parent's annotation —
      // amend overrides must satisfy it. Walk the parent chain when
      // either the property isn't declared locally OR is declared
      // without its own annotation.
      if found && annotation is Some(_) {
        annotation
      } else {
        match class_decl.parent_name {
          Some(parent_name) =>
            eval_class_property_annotation_with_depth(
              declarations,
              parent_name,
              property_name,
              depth + 1,
            )
          None => None
        }
      }
    }
    None => None
  }
}

///|
fn eval_class_property_annotation(
  declarations : Array[Declaration],
  class_name : String,
  property_name : String,
) -> String? {
  eval_class_property_annotation_with_depth(
    declarations, class_name, property_name, 0,
  )
}

///|

///|
/// PKL-148c: pretty-print a constraint text by re-inserting whitespace
/// around binary comparison / logical operators. `parse_type_text`
/// strips trivia so the captured constraint reads `this>=min` rather
/// than Apple Pkl's `this >= min`; this helper restores the spaces
/// before the text reaches the diagnostic.
fn pretty_constraint_text(text : String) -> String {
  let trimmed = pkl_constraint_trim(text)
  let multi : Array[String] = [">=", "<=", "==", "!=", "&&", "||"]
  let single : Array[Char] = ['>', '<']
  let buf = StringBuilder::new()
  let n = trimmed.length()
  let mut i = 0
  let mut depth = 0
  while i < n {
    let c = trimmed[i].to_int().unsafe_to_char()
    if c == '"' {
      // copy a string literal verbatim
      buf.write_char(c)
      i = i + 1
      while i < n {
        let cc = trimmed[i].to_int().unsafe_to_char()
        buf.write_char(cc)
        i = i + 1
        if cc == '\\' && i < n {
          buf.write_char(trimmed[i].to_int().unsafe_to_char())
          i = i + 1
        } else if cc == '"' {
          break
        }
      }
      continue
    }
    if c == '(' || c == '[' {
      depth = depth + 1
    } else if c == ')' || c == ']' {
      depth = depth - 1
    }
    if depth >= 0 {
      // PKL-148bb: arrow `->` needs symmetric spacing (`(it) -> body`),
      // not just trailing. Handle it ahead of the single-char `>` rule
      // so the `>` branch can't see the `-` first and leave the leading
      // gap unfilled.
      if c == '-' &&
        i + 1 < n &&
        trimmed[i + 1].to_int().unsafe_to_char() == '>' {
        ensure_trailing_space(buf)
        buf.write_string("->")
        buf.write_char(' ')
        i = i + 2
        continue
      }
      // PKL-148bb: commas in argument lists need a trailing space —
      // `parse_type_text` strips trivia between tokens, so the captured
      // text reads `(key,value)` instead of Apple Pkl's `(key, value)`.
      if c == ',' {
        buf.write_char(',')
        if i + 1 < n && trimmed[i + 1].to_int().unsafe_to_char() != ' ' {
          buf.write_char(' ')
        }
        i = i + 1
        continue
      }
      // Check multi-char ops first.
      let mut matched = false
      for op in multi {
        let m = op.length()
        if i + m <= n {
          let mut equal = true
          for j = 0; j < m; j = j + 1 {
            if trimmed[i + j] != op[j] {
              equal = false
              break
            }
          }
          if equal {
            ensure_trailing_space(buf)
            buf.write_string(op)
            buf.write_char(' ')
            i = i + m
            matched = true
            break
          }
        }
      }
      if matched {
        continue
      }
      let mut single_matched = false
      for op in single {
        if c == op {
          // Don't space a `>` that closes a `->` arrow (lambda
          // parameter list — `(it) -> body`), which would split the
          // arrow into `- >`.
          let current = buf.to_string()
          if op == '>' && current.length() > 0 {
            let last = current[current.length() - 1].to_int().unsafe_to_char()
            if last == '-' {
              buf.write_char(op)
              buf.write_char(' ')
              i = i + 1
              single_matched = true
              break
            }
          }
          // Don't space a `<` / `>` that's part of a generic type
          // argument list (rare in constraint texts, but defensive).
          ensure_trailing_space(buf)
          buf.write_char(op)
          buf.write_char(' ')
          i = i + 1
          single_matched = true
          break
        }
      }
      if single_matched {
        continue
      }
    }
    buf.write_char(c)
    i = i + 1
  }
  collapse_spaces(buf.to_string())
}

///|
fn ensure_trailing_space(buf : StringBuilder) -> Unit {
  let current = buf.to_string()
  if current.length() > 0 {
    let last = current[current.length() - 1].to_int().unsafe_to_char()
    if last != ' ' {
      buf.write_char(' ')
    }
  }
}

///|
fn collapse_spaces(text : String) -> String {
  let buf = StringBuilder::new()
  let mut prev_space = false
  for i = 0; i < text.length(); i = i + 1 {
    let c = text[i].to_int().unsafe_to_char()
    if c == ' ' {
      if !prev_space {
        buf.write_char(' ')
      }
      prev_space = true
    } else {
      buf.write_char(c)
      prev_space = false
    }
  }
  pkl_constraint_trim(buf.to_string())
}

///|
/// PKL-148c: walk an `Expr` tree and replace each bare
/// `Identifier(name)` whose name doesn't appear in the constraint's
/// known scope with `MemberAccess(Identifier("this"), name)`. Apple
/// Pkl's constraint expression body uses implicit-receiver lookup —
/// `abs` inside `Int(abs < 100)` resolves as `this.abs`. We pre-rewrite
/// instead of overloading the Identifier eval arm so the standard
/// resolver stays unchanged for non-constraint expressions.
fn rewrite_implicit_this_in_expr(
  expr : Expr,
  bindings : Array[Binding],
  env : Array[ValueBinding],
  cache : Array[ValueBinding],
) -> Expr {
  match expr {
    Identifier(name) => {
      if name == "this" ||
        name == "module" ||
        name == "true" ||
        name == "false" ||
        name == "null" {
        return expr
      }
      let in_bindings = find_binding(bindings, name) is Some(_)
      let in_env = lookup_value(env, name) is Some(_)
      let in_cache = lookup_value(cache, name) is Some(_)
      if in_bindings || in_env || in_cache {
        expr
      } else {
        MemberAccess(Identifier("this"), name)
      }
    }
    MemberAccess(target, name) =>
      MemberAccess(
        rewrite_implicit_this_in_expr(target, bindings, env, cache),
        name,
      )
    SafeMemberAccess(target, name) =>
      SafeMemberAccess(
        rewrite_implicit_this_in_expr(target, bindings, env, cache),
        name,
      )
    SubscriptAccess(target, key) =>
      SubscriptAccess(
        rewrite_implicit_this_in_expr(target, bindings, env, cache),
        rewrite_implicit_this_in_expr(key, bindings, env, cache),
      )
    CallExpr(callee, args) => {
      let new_callee = rewrite_implicit_this_in_expr(
        callee, bindings, env, cache,
      )
      let new_args : Array[Expr] = []
      for a in args {
        new_args.push(rewrite_implicit_this_in_expr(a, bindings, env, cache))
      }
      CallExpr(new_callee, new_args)
    }
    NullSafeCallExpr(callee, args) => {
      let new_callee = rewrite_implicit_this_in_expr(
        callee, bindings, env, cache,
      )
      let new_args : Array[Expr] = []
      for a in args {
        new_args.push(rewrite_implicit_this_in_expr(a, bindings, env, cache))
      }
      NullSafeCallExpr(new_callee, new_args)
    }
    UnaryExpr(op, inner) =>
      UnaryExpr(op, rewrite_implicit_this_in_expr(inner, bindings, env, cache))
    BinaryExpr(op, l, r) =>
      match op {
        Is | As =>
          BinaryExpr(
            op,
            rewrite_implicit_this_in_expr(l, bindings, env, cache),
            r,
          )
        _ =>
          BinaryExpr(
            op,
            rewrite_implicit_this_in_expr(l, bindings, env, cache),
            rewrite_implicit_this_in_expr(r, bindings, env, cache),
          )
      }
    NonNullExpr(inner) =>
      NonNullExpr(rewrite_implicit_this_in_expr(inner, bindings, env, cache))
    ConditionalExpr(c, t, e) =>
      ConditionalExpr(
        rewrite_implicit_this_in_expr(c, bindings, env, cache),
        rewrite_implicit_this_in_expr(t, bindings, env, cache),
        rewrite_implicit_this_in_expr(e, bindings, env, cache),
      )
    _ => expr
  }
}

///|
fn push_constraint_enclosing_member_bindings(
  target_env : Array[ValueBinding],
  members : Array[ValueMember],
  excluded_name : String,
) -> Unit {
  for value_member in members {
    if value_member.name == excluded_name {
      continue
    }
    if !is_invisible_member_name(value_member.name) {
      target_env.push({ name: value_member.name, value: value_member.value })
    } else if value_member.name.has_prefix(local_member_prefix) ||
      value_member.name.has_prefix(hidden_member_prefix) {
      let prefix_len = if value_member.name.has_prefix(local_member_prefix) {
        local_member_prefix.length()
      } else {
        hidden_member_prefix.length()
      }
      let bare = String::unsafe_substring(
        value_member.name,
        start=prefix_len,
        end=value_member.name.length(),
      )
      if bare != excluded_name {
        target_env.push({ name: bare, value: value_member.value })
      }
    }
  }
}

///|
/// PKL-148bb: element-wise constraint cascade for collection-typed
/// class properties. When the type is `Listing` /
/// `List` / `Set` / `Mapping` / `Map` and the inner element type carries
/// a predicate, evaluate the predicate with `this` bound to each
/// element / value. Returns Apple Pkl's `Type constraint
/// \`\` violated. Value: ` on the first miss; None
/// when every element satisfies (or the type has no inner predicate).
fn eval_collection_element_constraint_rejection_message(
  class_name : String,
  property_name : String,
  type_name : String,
  value : Value,
  enclosing_members : Array[ValueMember],
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  stack : Array[String],
  declarations : Array[Declaration],
  resolve_import : (String) -> EvalResult?,
) -> String? {
  let element_type = match generic_argument_text(type_name, "Listing") {
    Some(t) => Some(t)
    None =>
      match generic_argument_text(type_name, "List") {
        Some(t) => Some(t)
        None =>
          match generic_argument_text(type_name, "Set") {
            Some(t) => Some(t)
            None => generic_argument_text(type_name, "Collection")
          }
      }
  }
  match element_type {
    Some(inner) => {
      let inner_constraint = pkl_constrained_type_constraint_text(inner)
      match inner_constraint {
        Some(constraint_text) =>
          match value {
            ListingValue(elements)
            | DefaultedListingValue(_, elements, _)
            | ListValue(elements)
            | SetValue(elements) =>
              for element in elements {
                let parts = pkl_split_constraint_arguments(constraint_text)
                for part in parts {
                  let pred_expr = match parse_constraint_expression(part) {
                    Some(e) => e
                    None => continue
                  }
                  let pred_env = copy_value_bindings(env)
                  pred_env.push({ name: "this", value: element })
                  let super_diags : Array[Diagnostic] = []
                  match lookup_class_binding(class_env, class_name) {
                    Some(class_binding) =>
                      match class_binding.parent_name {
                        Some(parent_name) => {
                          let aliases = eval_type_alias_bindings(declarations)
                          let resolved_parent = eval_resolved_type_alias(
                            parent_name, aliases,
                          )
                          let super_members = eval_class_default_members(
                            resolved_parent, bindings, env, class_env, cache, stack,
                            declarations, super_diags, resolve_import,
                          )
                          pred_env.push({
                            name: "super",
                            value: ObjectValue(super_members),
                          })
                        }
                        None => ()
                      }
                    None => ()
                  }
                  match element {
                    ObjectValue(receiver_members) =>
                      for value_member in receiver_members {
                        if !is_invisible_member_name(value_member.name) {
                          pred_env.push({
                            name: value_member.name,
                            value: value_member.value,
                          })
                        }
                      }
                    _ => ()
                  }
                  push_constraint_enclosing_member_bindings(
                    pred_env, enclosing_members, property_name,
                  )
                  let rewritten = rewrite_implicit_this_in_expr(
                    pred_expr, bindings, pred_env, cache,
                  )
                  let probe_diags : Array[Diagnostic] = []
                  let mut probe = eval_expr_with_bindings(
                    rewritten, bindings, pred_env, class_env, cache, stack, declarations,
                    probe_diags, resolve_import,
                  )
                  if probe is Some(FunctionValue(_, _, _, _, _)) {
                    let apply_diags : Array[Diagnostic] = []
                    let apply_result = eval_expr_with_bindings(
                      CallExpr(MemberAccess(rewritten, "apply"), [
                        Identifier("this"),
                      ]),
                      bindings,
                      pred_env,
                      class_env,
                      cache,
                      stack,
                      declarations,
                      apply_diags,
                      resolve_import,
                    )
                    if apply_diags.length() == 0 {
                      probe = apply_result
                    } else if apply_diags[0].message.has_prefix(
                        "Expected value of type",
                      ) {
                      return Some(apply_diags[0].message)
                    }
                  }
                  match probe {
                    Some(BoolValue(true)) => continue
                    Some(BoolValue(false)) =>
                      return Some(
                        "Type constraint `\{pretty_constraint_text(part)}` violated. Value: \{render_pcf_value_inline(element)}",
                      )
                    _ => continue
                  }
                }
              }
            _ => ()
          }
        None => ()
      }
      return None
    }
    None => ()
  }
  None
}

///|
/// PKL-148c: parse a constraint expression text into an `Expr`. Uses
/// the regular parser by wrapping the source as `__probe = (text)` and
/// pulling the binding's value back out. Returns `None` when the parse
/// fails or the binding is missing — the caller falls through to the
/// existing static predicate path.
fn parse_constraint_expression(text : String) -> Expr? {
  let wrapped = "__probe = (" + text + ")"
  let parsed = parse_source(wrapped)
  for binding in parsed.program.bindings {
    if binding.name == "__probe" {
      return Some(binding.value)
    }
  }
  None
}

///|
fn constraint_function_collection_parameter_error(
  fn_value : Value,
  argument_value : Value,
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  stack : Array[String],
  declarations : Array[Declaration],
  resolve_import : (String) -> EvalResult?,
) -> String? {
  match fn_value {
    FunctionValue(parameters, _, _, captured_env, _) => {
      if parameters.length() != 1 {
        return None
      }
      let parameter_type_name = match
        strip_lazy_collection_annotation_marker_opt(parameters[0].type_name) {
        Some(name) => name
        None => return None
      }
      if type_annotation_collection_branch_count(
          parameter_type_name, declarations,
        ) ==
        0 {
        return None
      }
      let call_cache = copy_value_bindings(captured_env)
      push_module_metadata_from_cache(call_cache, cache)
      match
        cast_value_to_type_annotation(
          parameter_type_name, argument_value, bindings, env, class_env, call_cache,
          stack, declarations, resolve_import,
        ) {
        TypeCastOk(casted) =>
          match
            binding_collection_host_constraint_rejection_message(
              Some(parameter_type_name),
              casted,
              declarations,
            ) {
            Some(message) => Some(message)
            None =>
              match first_deferred_error_message(casted) {
                Some(message) =>
                  Some(
                    qualify_collection_parameter_error_message(
                      message, parameter_type_name, class_env, call_cache, declarations,
                    ),
                  )
                None => None
              }
          }
        TypeCastErr(message) => Some(message)
      }
    }
    _ => None
  }
}

///|
fn collection_parameter_value_type_name(
  annotation : String,
  declarations : Array[Declaration],
) -> String? {
  let aliases = eval_type_alias_bindings(declarations)
  let resolved = eval_resolved_type_alias(
    strip_lazy_collection_annotation_marker(annotation),
    aliases,
  )
  let normalized = strip_balanced_outer_type_parens(
    pkl_strip_default_type_marker(pkl_constraint_trim(resolved)),
  )
  let base = match pkl_constrained_type_base_name(normalized) {
    Some(name) => name
    None => normalized
  }
  let inner = match generic_argument_text(base, "Listing") {
    Some(text) => Some(text)
    None =>
      match generic_argument_text(base, "List") {
        Some(text) => Some(text)
        None =>
          match generic_argument_text(base, "Set") {
            Some(text) => Some(text)
            None =>
              match generic_argument_text(base, "Collection") {
                Some(text) => Some(text)
                None =>
                  match generic_argument_text(base, "Mapping") {
                    Some(text) => {
                      let parts = split_top_level_generic_arguments(text)
                      if parts.length() == 2 {
                        Some(parts[1])
                      } else {
                        None
                      }
                    }
                    None =>
                      match generic_argument_text(base, "Map") {
                        Some(text) => {
                          let parts = split_top_level_generic_arguments(text)
                          if parts.length() == 2 {
                            Some(parts[1])
                          } else {
                            None
                          }
                        }
                        None => None
                      }
                  }
              }
          }
      }
  }
  match inner {
    Some(text) => {
      let cleaned = strip_balanced_outer_type_parens(
        pkl_strip_default_type_marker(pkl_constraint_trim(text)),
      )
      Some(
        match pkl_constrained_type_base_name(cleaned) {
          Some(name) => name
          None => cleaned
        },
      )
    }
    None => None
  }
}

///|
fn qualify_collection_parameter_error_message(
  message : String,
  annotation : String,
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  declarations : Array[Declaration],
) -> String {
  let expected = match
    collection_parameter_value_type_name(annotation, declarations) {
    Some(name) => name
    None => return message
  }
  let module_name = match module_name_from_cache(cache) {
    Some(name) => name
    None => return message
  }
  if lookup_class_binding(class_env, expected) is None {
    return message
  }
  let prefix = "Expected value of type `\{expected}`"
  if !message.has_prefix(prefix) {
    return message
  }
  "Expected value of type `\{module_name}#\{expected}`" +
  String::unsafe_substring(message, start=prefix.length(), end=message.length())
}

///|
fn push_constraint_method_scope(
  constraint_cache : Array[ValueBinding],
  class_name : String,
  source_name : String,
  property_value : Value,
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
) -> Unit {
  push_sibling_class_methods(
    constraint_cache, class_name, class_env, env, cache,
  )
  match property_value {
    ObjectValue(receiver_members) => {
      push_receiver_method_bindings(constraint_cache, receiver_members)
      match find_object_class_tag(receiver_members) {
        Some(receiver_class_name) =>
          push_sibling_class_methods(
            constraint_cache, receiver_class_name, class_env, env, constraint_cache,
          )
        None => ()
      }
    }
    _ => ()
  }
  match pkl_constrained_type_base_name(source_name) {
    Some(base_name) =>
      push_sibling_class_methods(
        constraint_cache, base_name, class_env, env, constraint_cache,
      )
    None => ()
  }
}

///|
/// PKL-148c: run an arbitrary constraint expression against a candidate
/// value with `this` bound. When the candidate is an `ObjectValue`, the
/// object's members are also hoisted into the env so the implicit
/// receiver form (`street.endsWith("St.")` rather than
/// `this.street.endsWith("St.")`) resolves. Returns the canonical
/// diagnostic text when the predicate evaluates to `false`.
fn eval_runtime_constraint_for_property(
  class_name : String,
  property_name : String,
  property_value : Value,
  enclosing_members : Array[ValueMember],
  bindings : Array[Binding],
  env : Array[ValueBinding],
  class_env : Array[ClassBinding],
  cache : Array[ValueBinding],
  stack : Array[String],
  declarations : Array[Declaration],
  resolve_import : (String) -> EvalResult?,
) -> String? {
  let source_name = match
    eval_class_property_annotation(declarations, class_name, property_name) {
    Some(s) => s
    None => return None
  }
  // PKL-148bb: when the property type is a `Collection`
  // (or List/Set/Listing/Mapping/Map) and the inner type carries its
  // own constraint, fire the inner predicate against each element /
  // entry. The structural element walker only checks the bare type
  // head; the inner constraint stays untouched until this pass.
  // `classes/setConstraints1` exercises `ys: Set`.
  match
    eval_collection_element_constraint_rejection_message(
      class_name, property_name, source_name, property_value, enclosing_members,
      bindings, env, class_env, cache, stack, declarations, resolve_import,
    ) {
    Some(message) => return Some(message)
    None => ()
  }
  let text = match pkl_constrained_type_constraint_text(source_name) {
    Some(t) => t
    None => return None
  }
  let parts = pkl_split_constraint_arguments(text)
  for part in parts {
    let expr = match parse_constraint_expression(part) {
      Some(e) => e
      None => continue
    }
    // Build a fresh env with `this` bound to the candidate. If the
    // candidate is an ObjectValue, push its members under their bare
    // names too so identifier lookup picks them up before falling
    // through to the surrounding scope (implicit receiver). The
    // enclosing class's other property values are also added so a
    // sibling reference (`Int(this >= min)`) resolves.
    let new_env : Array[ValueBinding] = []
    for binding in env {
      new_env.push(binding)
    }
    new_env.push({ name: "this", value: property_value })
    // PKL-148b: bind `super` to the parent class's resolved defaults so
    // a constraint like `Int(this > super.x)` can read inherited values
    // from inside an amend chain. The parent chain is resolved through
    // `eval_class_default_members`, mirroring the same `super` shape
    // that `eval_class_default_members` plumbs into the defaults cache.
    let super_diags : Array[Diagnostic] = []
    match lookup_class_binding(class_env, class_name) {
      Some(class_binding) =>
        match class_binding.parent_name {
          Some(parent_name) => {
            let aliases = eval_type_alias_bindings(declarations)
            let resolved_parent = eval_resolved_type_alias(parent_name, aliases)
            let super_members = eval_class_default_members(
              resolved_parent, bindings, env, class_env, cache, stack, declarations,
              super_diags, resolve_import,
            )
            new_env.push({ name: "super", value: ObjectValue(super_members) })
          }
          None => ()
        }
      None => ()
    }
    match property_value {
      ObjectValue(receiver_members) =>
        for value_member in receiver_members {
          if !is_invisible_member_name(value_member.name) {
            new_env.push({ name: value_member.name, value: value_member.value })
          }
        }
      _ => ()
    }
    // PKL-148b: a class body's `local` / `hidden` declarations are
    // visible inside that class's constraint expressions, e.g.
    // `local isValid = (n) -> n > x; y: Int(isValid)`. The members
    // sit in `enclosing_members` under their storage-prefixed name
    // (`@local$isValid` / `@hidden$isValid`); strip the prefix so
    // bare-name resolution inside the constraint succeeds.
    push_constraint_enclosing_member_bindings(
      new_env, enclosing_members, property_name,
    )
    let constraint_cache = copy_value_bindings(cache)
    push_constraint_method_scope(
      constraint_cache, class_name, source_name, property_value, new_env, class_env,
      cache,
    )
    // PKL-148c: rewrite bare `Identifier(name)` nodes inside the
    // constraint expression to `MemberAccess(Identifier("this"), name)`
    // when `name` is neither a binding nor in env. Apple Pkl treats
    // `abs` inside `Int(abs < 100)` as `this.abs` because the
    // candidate value is the implicit receiver of the constraint
    // body. Pre-rewriting the tree keeps the existing
    // `eval_expr_with_bindings` flow untouched.
    let rewritten = rewrite_implicit_this_in_expr(
      expr, bindings, new_env, constraint_cache,
    )
    let probe_diags : Array[Diagnostic] = []
    let mut probe_result = eval_expr_with_bindings(
      rewritten, bindings, new_env, class_env, constraint_cache, stack, declarations,
      probe_diags, resolve_import,
    )
    // Apple Pkl treats a constraint-position function reference as a
    // unary call applied to the candidate (`Int(isValid)` ≡
    // `Int(it -> isValid(it))`). When the rewritten predicate
    // evaluates to a FunctionValue rather than a Boolean, replay the
    // probe as `function.apply(this)` so the predicate's result drives
    // the rejection.
    let mut had_function_apply = false
    if probe_result is Some(FunctionValue(_, _, _, _, _) as fn_value) {
      match
        constraint_function_collection_parameter_error(
          fn_value, property_value, bindings, new_env, class_env, constraint_cache,
          stack, declarations, resolve_import,
        ) {
        Some(message) => return Some(message)
        None => ()
      }
      had_function_apply = true
      let apply_diags : Array[Diagnostic] = []
      let apply_result = eval_expr_with_bindings(
        CallExpr(MemberAccess(rewritten, "apply"), [Identifier("this")]),
        bindings,
        new_env,
        class_env,
        constraint_cache,
        stack,
        declarations,
        apply_diags,
        resolve_import,
      )
      if apply_diags.length() == 0 {
        probe_result = apply_result
      } else if apply_diags[0].message.has_prefix("Expected value of type") {
        // PKL-148bh: the apply failed because the predicate's own
        // parameter type rejected the candidate. Apple Pkl surfaces
        // that inner diagnostic verbatim (classes/constraints13:
        // `Listing` parameter rejects `Int` element via the
        // standard "Expected value of type ..." wording). Only
        // surface this specific shape so unrelated failures inside
        // the predicate body (e.g. an unimplemented stdlib method)
        // stay silent like they did before.
        return Some(apply_diags[0].message)
      }
    }
    match probe_result {
      Some(BoolValue(true)) => ()
      Some(BoolValue(false)) => {
        // PKL-148d: the diagnostic's `Value:` segment renders
        // ObjectValue candidates with their declared class name
        // (`new Address { ... }` rather than `new { ... }`), matching
        // Apple Pkl's compact-line form.
        let hint = match pkl_constrained_type_base_name(source_name) {
          Some(base) =>
            if base == "Int" ||
              base == "Float" ||
              base == "Number" ||
              base == "Boolean" ||
              base == "String" ||
              base == "Listing" ||
              base == "Mapping" ||
              base == "Set" ||
              base == "Map" ||
              base.has_prefix("Listing<") ||
              base.has_prefix("Mapping<") ||
              base.has_prefix("Set<") ||
              base.has_prefix("Map<") {
              None
            } else {
              Some(base)
            }
          None => None
        }
        return Some(
          "Type constraint `\{pretty_constraint_text(part)}` violated. Value: \{render_pcf_value_inline_compact(property_value, hint)}",
        )
      }
      // PKL-148bb: Apple Pkl rejects a constraint predicate that
      // evaluates to anything other than Boolean / Function with
      // `Expected value of type \`Boolean\` or \`Function\`, but got
      // type \`\`. Value: `. After the `function.apply(this)`
      // replay above, the wording narrows to just `\`Boolean\``
      // because the function reference was already resolved
      // (`classes/constraints9`: `Int("not a boolean")` vs
      // `Int((x) -> "not a boolean")`).
      //
      // A trailing `FunctionValue` here means the apply replay landed
      // diagnostics and we kept the raw probe; fall through silently
      // so the static-cascade fallback for predicates whose function
      // reference does resolve at runtime stays intact
      // (`classes/constraints10`).
      Some(FunctionValue(_, _, _, _, _)) => ()
      Some(non_bool_value) => {
        let actual = eval_value_type_name(non_bool_value)
        let expected = if had_function_apply {
          "`Boolean`"
        } else {
          "`Boolean` or `Function`"
        }
        return Some(
          "Expected value of type \{expected}, but got type `\{actual}`. Value: \{render_pcf_value_inline(non_bool_value)}",
        )
      }
      None => ()
    }
  }
  None
}

///|
fn eval_class_property_constraint_value_rejection_message(
  class_name : String,
  property_name : String,
  value : Value,
  declarations : Array[Declaration],
) -> String? {
  match
    eval_class_property_annotation(declarations, class_name, property_name) {
    Some(source_name) => {
      let display_name = "\{class_name} member \{property_name}"
      let aliases = eval_type_alias_bindings(declarations)
      let constraint_source_name = match
        eval_constrained_type_source_name(source_name, aliases) {
        Some(resolved) => resolved
        None => source_name
      }
      // PKL-148b: a class property typed `Listing<...>(...)` whose
      // amend body is empty (`l {}`) initially evaluates to an empty
      // ObjectValue. Coerce to ListingValue before the constraint
      // dispatch so the Listing-host predicate (e.g. `!isEmpty`) fires.
      let coerced = coerce_value_to_annotated_type(
        value,
        Some(constraint_source_name),
      )
      match
        pkl_constrained_type_annotation_value_rejection_message_from_source(
          display_name, constraint_source_name, coerced,
        ) {
        Some(message) => Some(message)
        None =>
          pkl_user_defined_constrained_type_annotation_value_rejection_message_from_source(
            display_name, source_name, coerced, declarations,
          )
      }
    }
    None => None
  }
}

///|
/// PKL-148i: reject an assignment whose value's runtime type doesn't
/// satisfy the property's declared type annotation. The existing
/// `eval_class_property_constraint_value_rejection_message` only
/// dispatches predicate-style constraints (`Int(x > 0)`), so a bare
/// type annotation like `name: String` never fires when the supplied
/// value is the wrong shape (`new Person { name = 42 }` evaluated to
/// `42` instead of producing the upstream rejection diagnostic).
/// Mirrors the surface of `eval_callable_return_rejection_message`:
/// resolve type alias, accept type parameters, and only emit when
/// neither the builtin acceptance set nor a user-class annotation
/// satisfies the value.
fn eval_class_property_type_rejection_message(
  class_name : String,
  property_name : String,
  value : Value,
  declarations : Array[Declaration],
) -> String? {
  match
    eval_class_property_annotation(declarations, class_name, property_name) {
    Some(source_name) => {
      if eval_type_name_is_type_parameter(source_name, declarations) {
        return None
      }
      if reference_value_satisfies_annotation(source_name, value, declarations) {
        return None
      }
      // PKL-148bb: arity mismatch on a function-typed property (`f:
      // () -> Int` amended with `(str) -> str.length`) projects to
      // `Expected value of type \`FunctionN\`, but got type
      // \`FunctionM\`. Value: new FunctionM {}` (`classes/lambdaConstraints1`).
      match (function_type_arity(source_name), value) {
        (Some(expected), FunctionValue(params, _, _, _, _)) => {
          let actual = params.length()
          if expected != actual {
            return Some(
              "Expected value of type `Function\{expected}`, but got type `Function\{actual}`. Value: new Function\{actual} {}",
            )
          }
        }
        _ => ()
      }
      let aliases = eval_type_alias_bindings(declarations)
      let resolved_type_name = eval_resolved_type_alias(source_name, aliases)
      let base = match pkl_constrained_type_base_name(resolved_type_name) {
        Some(b) => b
        None => resolved_type_name
      }
      let coerced = coerce_value_to_annotated_type(value, Some(source_name))
      let choices = split_top_level_union_choices(base)
      if choices.length() > 1 {
        let mut any_known_head = false
        for choice in choices {
          let trimmed = pkl_strip_default_type_marker(
            pkl_constraint_trim(choice),
          )
          let without_constraint = match
            pkl_constrained_type_base_name(trimmed) {
            Some(b) => b
            None => trimmed
          }
          let stripped_q = if without_constraint.has_suffix("?") {
            String::unsafe_substring(
              without_constraint,
              start=0,
              end=without_constraint.length() - 1,
            )
          } else {
            without_constraint
          }
          let head = {
            let mut cut = -1
            let n = stripped_q.length()
            for i = 0; i < n; i = i + 1 {
              if stripped_q[i].to_int().unsafe_to_char() == '<' {
                cut = i
                break
              }
            }
            if cut < 0 {
              stripped_q
            } else {
              String::unsafe_substring(stripped_q, start=0, end=cut)
            }
          }
          let head_with_optional = if without_constraint.has_suffix("?") {
            head + "?"
          } else {
            head
          }
          if eval_value_accepts_type_annotation(head_with_optional, coerced) {
            return eval_resolved_collection_element_structural_rejection_message(
              stripped_q, coerced, declarations,
            )
          }
          if value_satisfies_user_class_annotation(head, coerced, declarations) {
            return None
          }
          if is_stdlib_class_name(head) ||
            eval_lookup_class_decl(declarations, head) is Some(_) {
            any_known_head = true
          }
        }
        if !any_known_head {
          return None
        }
        let diag_name = rejection_type_label(base)
        if coerced is NullValue {
          return Some("Expected value of type `\{diag_name}`, but got `null`.")
        }
        return Some(
          "Expected value of type `\{diag_name}`, but got type `\{eval_value_type_name(coerced)}`. Value: \{render_pcf_value_inline(coerced)}",
        )
      }
      // Normalize generic type heads (`Listing` → `Listing`,
      // `Mapping` → `Mapping`, `Pair` → `Pair`, …) before
      // the acceptance check; `eval_value_accepts_type_annotation`
      // matches against the bare collection name.
      let stripped_q = if base.has_suffix("?") {
        String::unsafe_substring(base, start=0, end=base.length() - 1)
      } else {
        base
      }
      let head = {
        let mut cut = -1
        let n = stripped_q.length()
        for i = 0; i < n; i = i + 1 {
          if stripped_q[i].to_int().unsafe_to_char() == '<' {
            cut = i
            break
          }
        }
        if cut < 0 {
          stripped_q
        } else {
          String::unsafe_substring(stripped_q, start=0, end=cut)
        }
      }
      let head_with_optional = if base.has_suffix("?") {
        head + "?"
      } else {
        head
      }
      if eval_value_accepts_type_annotation(head_with_optional, coerced) {
        // Structural element check for generic collections — Apple Pkl
        // surfaces `Expected value of type \`Int\`...` for
        // `xs: List` amended with `List("one")`.
        return eval_resolved_collection_element_structural_rejection_message(
          stripped_q, coerced, declarations,
        )
      }
      if value_satisfies_user_class_annotation(head, coerced, declarations) {
        return None
      }
      // Only reject for stdlib type names or known user classes;
      // unknown annotations stay silent (matches the legacy behaviour
      // for not-yet-implemented surface).
      if !is_stdlib_class_name(head) &&
        !(eval_lookup_class_decl(declarations, head) is Some(_)) {
        return None
      }
      let diag_name = rejection_type_label(base)
      if coerced is NullValue {
        return Some("Expected value of type `\{diag_name}`, but got `null`.")
      }
      Some(
        "Expected value of type `\{diag_name}`, but got type `\{eval_value_type_name(coerced)}`. Value: \{render_pcf_value_inline(coerced)}",
      )
    }
    None => None
  }
}

///|
fn eval_object_member_expr_is_provided(
  members : Array[ObjectMember],
  name : String,
) -> Bool {
  for field in members {
    if field.name == name {
      return true
    }
  }
  false
}

///|
fn eval_class_property_default_constraints_are_valid_with_depth(
  class_name : String,
  provided_members : Array[ObjectMember],
  values : Array[ValueMember],
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
  depth : Int,
) -> Bool {
  if depth > 8 {
    return true
  }
  match eval_lookup_class_decl(declarations, class_name) {
    Some(class_decl) => {
      let mut ok = true
      match class_decl.parent_name {
        Some(parent_name) =>
          if !eval_class_property_default_constraints_are_valid_with_depth(
              parent_name,
              provided_members,
              values,
              declarations,
              diagnostics,
              depth + 1,
            ) {
            ok = false
          }
        None => ()
      }
      for property in class_decl.properties {
        if property.value is Some(_) &&
          !eval_object_member_expr_is_provided(provided_members, property.name) {
          match lookup_member(values, property.name) {
            Some(default_value) =>
              match
                eval_class_property_constraint_value_rejection_message(
                  class_name,
                  property.name,
                  default_value,
                  declarations,
                ) {
                Some(message) => {
                  diagnostics.push(diag(message))
                  ok = false
                }
                None => ()
              }
            None => ()
          }
        }
      }
      ok
    }
    None => true
  }
}

///|
fn eval_class_property_default_constraints_are_valid(
  class_name : String,
  provided_members : Array[ObjectMember],
  values : Array[ValueMember],
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
) -> Bool {
  eval_class_property_default_constraints_are_valid_with_depth(
    class_name, provided_members, values, declarations, diagnostics, 0,
  )
}

///|
fn eval_expr_class_property_constraints_are_valid(
  expr : Expr,
  value : Value,
  declarations : Array[Declaration],
  diagnostics : Array[Diagnostic],
) -> Bool {
  match (expr, value) {
    (TypedObjectLiteral(class_name, members), ObjectValue(values)) => {
      let mut ok = true
      for field in members {
        match lookup_value_member(values, field.name) {
          // Property thunks own their type and class-constraint checks.
          // Keeping the handle unresolved here preserves lazy object
          // construction; the same checks run exactly once on first force.
          Some({ value: ThunkValue(_), .. }) => ()
          Some({ value: member_value, .. }) => {
            match
              eval_class_property_constraint_value_rejection_message(
                class_name,
                field.name,
                member_value,
                declarations,
              ) {
              Some(message) => {
                diagnostics.push(diag(message))
                ok = false
              }
              None => ()
            }
            if !eval_expr_class_property_constraints_are_valid(
                field.value,
                member_value,
                declarations,
                diagnostics,
              ) {
              ok = false
            }
          }
          None => ()
        }
      }
      if !eval_class_property_default_constraints_are_valid(
          class_name, members, values, declarations, diagnostics,
        ) {
        ok = false
      }
      ok
    }
    (ObjectLiteral(members), ObjectValue(values)) => {
      let mut ok = true
      for field in members {
        match lookup_value_member(values, field.name) {
          // Alias validation for a pending property is likewise deferred to
          // the thunk's annotated-type validation path.
          Some({ value: ThunkValue(_), .. }) => ()
          Some({ value: member_value, .. }) =>
            if !eval_expr_class_property_constraints_are_valid(
                field.value,
                member_value,
                declarations,
                diagnostics,
              ) {
              ok = false
            }
          None => ()
        }
      }
      ok
    }
    _ => true
  }
}

///|
fn eval_object_member_alias_constraints_are_valid(
  members : Array[ObjectMember],
  values : Array[ValueMember],
  aliases : Array[EvalTypeAliasBinding],
  diagnostics : Array[Diagnostic],
) -> Bool {
  let mut ok = true
  for object_member in members {
    match lookup_value_member(values, object_member.name) {
      Some({ value: ThunkValue(_), .. }) => ()
      Some({ value, .. }) => {
        if !eval_constrained_type_annotation_value_is_valid(
            object_member.type_name,
            value,
            aliases,
            diagnostics,
          ) {
          ok = false
        }
        if !eval_expr_alias_constraints_are_valid(
            object_member.value,
            value,
            aliases,
            diagnostics,
          ) {
          ok = false
        }
      }
      None => ()
    }
  }
  ok
}

///|
fn eval_expr_alias_constraints_are_valid(
  expr : Expr,
  value : Value,
  aliases : Array[EvalTypeAliasBinding],
  diagnostics : Array[Diagnostic],
) -> Bool {
  match (expr, value) {
    (ObjectLiteral(members), ObjectValue(values)) =>
      eval_object_member_alias_constraints_are_valid(
        members, values, aliases, diagnostics,
      )
    (TypedObjectLiteral(_, members), ObjectValue(values)) =>
      eval_object_member_alias_constraints_are_valid(
        members, values, aliases, diagnostics,
      )
    _ => true
  }
}