Repository navigation
Description
Compiler version
kairo-0.1.1+dev.20261024
Compiler stage
Stage 1 (self-hosted)
Host platform
Linux (x86_64)
Minimal reproduction
// Tests/Bugs/Triage/F3-generic-instance-lifecycle-facts-unknown.k
// build: kairo F3-generic-instance-lifecycle-facts-unknown.k (expect exit 0)
class <T> Box { pub var v: T }
// Box<i32> is trivially copyable, but its facts are read off the pattern
// Box<T>, where they are Unknown.
enum W { Has { b: Box<i32> }, Empty }
fn main() -> i32 {
var w = W::Has { b: Box<i32>() }
return 0
}Command line
build/x86_64-linux-gnu/debug/bin/kairo --resource-dir=build/llvm/lib/clang/22 --builtins-dir=Lib/builtin --std-dir=Lib/std Tests/Bugs/Triage/F3-generic-instance-lifecycle-facts-unknown.kActual behavior
error @ Tests/Bugs/Triage/F3-generic-instance-lifecycle-facts-unknown.k:6
4 | // Box<i32> is trivially copyable, but its facts are read off the pattern
5 | // Box<T>, where they are Unknown.
6 | enum W { Has { b: Box<i32> }, Empty }
| ^ ^^^^^^^^^^^
| [SC116E] an enum whose payload's lifecycle is not known until instantiation is not yet supported
note @ Tests/Bugs/Triage/F3-generic-instance-lifecycle-facts-unknown.k:6
4 | // Box<i32> is trivially copyable, but its facts are read off the pattern
5 | // Box<T>, where they are Unknown.
6 | enum W { Has { b: Box<i32> }, Empty }
| ^ 'b' of variant 'Has' has type 'Box<i32>', whose copy, move and destruction are not known until instantiation
Exit code 1.Expected behavior
The program compiles and exits 0. A generic instance gets lifecycle facts computed from its substituted field, payload and base types: Box<i32> is trivially copyable and destructible, so W can be laid out. --print-sema should print a lifecycle row for the instance (Box<i32>: trivial) instead of only the pattern's all-? row.
Additional context
SC116E is a deliberate interim gate, not a regression: it is asserted today by Tests/Sema/Check/adt_unknown_facts.k, and it goes away with this work.
ClassLifecycle (Compiler/Sema/Check/ClassLifecycle.k) computes facts on the PATTERN only. A fact that depends on a generic parameter is Unknown, and every instance reads its pattern's facts, so Box<i32> reads all-Unknown. --print-sema on the repro prints only
lifecycle Box: category=? trivial_copy=? trivial_dtor=? destructible=?
lifecycle W: category=? trivial_copy=? trivial_dtor=? destructible=?
Every rule that reads facts skips Unknown, with a comment pointing at this work: SC056E (virtual destructor), SC061E (base/derived category), SC051E (movability). Those rules see nothing in generic code. The same goes for transfer checking in a generic body (see the transfer-sites ticket).
Suggested fix: when Mono fills an instance, compute its facts from the substituted types, memoize them on the instance decl, and re-run the skipped rules on the instance. Then remove the SC116E gate for non-generic ADTs (the Unknown case in EnumLayoutLowering's non-generic path), and flip Tests/Sema/Check/adt_unknown_facts.k to a codegen test.
Tests: --print-sema lifecycle rows for Opt<i32> (trivial) vs an Opt<Mv> with a MOVE payload (trivial_copy=0); SC056E on a class with a non-virtual base that holds Opt<Mv>, none for Opt<i32>.
Activity
Ze7111 commentedon Nov 9, 2026
a non-generic enum over a concrete generic instance (Box) reading its pattern's unknown lifecycle facts and failing with SC116E, fixed in commit f488671