Predicate Reflection¶
Accepted
Accepted for V1 conformance, structural-satisfaction, and primitive type-kind predicates; negative facts, runtime value narrowing, and broader predicate logic are deferred.
Type.Predicate is a comptime-only value for reflection checks that can carry facts into a guarded declaration or comptime branch.
V1 fact-bearing predicates include:
- semantic conformance facts from
T.implements(C) - structural satisfaction facts from
T.satisfies(Shape) - type-kind facts from primitive
Typeclassifiers such asT.is_concrete(),T.is_constraint(),T.is_sized(),T.has_fields(), andT.is_dyn() - dyn-safe contract facts from
C.is_dyn_safe()
Simple property checks return bool when a true result does not grant sema a reusable fact. Primitive Type kind classifiers may return Type.Predicate because branch-local facts such as concrete, constraint, sized, aggregate, field-bearing type, structural constraint, semantic contract, or dyn erased type can make later reflection and type checking more precise.
Dyn-safety is a property of an applied semantic contract surface. C.is_dyn_safe() returns a Type.Predicate; when true, it carries a .dyn_safe_contract fact for the checked contract C. For wrong targets, including non-contract types, structural constraints, mixed intersections, not-fully-applied contracts, and already erased dyn C types, is_dyn_safe() returns false and carries no fact. The metadata API C.dyn_safety() reports structured wrong-target and failure details.
Predicate and Metadata APIs¶
Conformance lookup has a predicate API and a metadata API:
T.implements(Contract, scope: ?Scope = null) Type.Predicate
T.conformance(Contract, scope: ?Scope = null) Type.Conformance!Type.ConformanceLookupError
Structural satisfaction follows the same split:
T.satisfies(Shape, scope: ?Scope = null) Type.Predicate
T.satisfaction(Shape, scope: ?Scope = null) Type.Satisfaction!Type.SatisfactionError
Predicate APIs answer whether a fact is true and may carry that fact into sema. Metadata APIs return resolved details and structured failures for diagnostics, tooling, and deeper comptime logic.
In V1, T must be a concrete type for implements and satisfies. Checking whether one constraint implies another is deferred.
Lookup Scope¶
Visibility-dependent reflection APIs take an optional comptime Scope value with a default of null. This is a default parameter, not a function overload set:
T.implements(C) // omits the defaulted scope argument
T.implements(C, scope)
When the argument is null, the public reflection API resolves the lookup scope by evaluating Scope.caller() in the called API body before it delegates further. Scope.caller() is not used as a default parameter expression; default parameter expressions cannot call Scope.caller().
Scope.caller() is available only inside functions evaluated at comptime. It returns the lexical/module visibility scope of the direct call expression. It is one frame only; library code that wants to preserve caller visibility through internal calls must capture Scope.caller() at the public boundary and pass the scope explicitly.
Scope.current() returns the lexical/module visibility scope where it appears. At top level, use Scope.current().
Scope is a comptime-only value. It may be stored in consts, compared for identity, and passed to reflection APIs. It may expose stable diagnostic metadata such as module or declaration names, but not unstable compiler-internal IDs. It is not a runtime value and does not grant mutation authority.
Fact Model¶
Type.Predicate is inspectable and passable through comptime code:
const Predicate = struct {
fn value(self: Self) bool
fn facts_when_true(self: Self) Type.PredicateFacts
fn facts_when_false(self: Self) Type.PredicateFacts
}
Fact-bearing predicates are not directly aggregate-constructible. They are produced by compiler-checked reflection operations such as T.implements(...) and T.satisfies(...).
A factless predicate may be created from a boolean:
Type.Predicate.from_bool(condition)
Predicate facts are ordinary read-only reflection metadata:
const PredicateFacts = struct {
implements: []const Type.ImplementsFact
satisfies: []const Type.SatisfiesFact
type_kinds: []const Type.TypeKindFact
dyn_safe_contracts: []const Type.DynSafeContractFact
}
const ImplementsFact = struct {
subject: Type
contract: Type
scope: Scope
}
const SatisfiesFact = struct {
subject: Type
shape: Type
scope: Scope
}
const TypeKind = enum {
concrete,
constraint,
sized,
structural,
contract,
aggregate,
has_fields,
dyn_erased,
optional,
plain_enum,
enum_union,
}
const TypeKindFact = struct {
subject: Type
kind: Type.TypeKind
}
const DynSafeContractFact = struct {
contract: Type
}
Each fact category has its own metadata shape, so V1 does not use nullable fields whose validity depends on a separate tag. Empty categories are represented by empty slices.
Fact identity is category-specific:
- implements facts use
(subject, contract, scope); - satisfies facts use
(subject, shape, scope); - type-kind facts use
(subject, kind); - dyn-safe contract facts use
(contract).
Combining predicates deduplicates facts within each category deterministically, preserving first occurrence where possible. Category display order is implements, satisfies, type_kinds, then dyn_safe_contracts.
A future concrete type union design may replace Type.PredicateFacts with a flat fact list, such as []const Type.Fact where Type.Fact = ImplementsFact | SatisfiesFact | TypeKindFact | DynSafeContractFact. See CEP-0001: Concrete Type Unions.
facts_when_false exists for future negative or alternative narrowing. V1 producers normally leave it empty, and V1 sema does not use negative facts.
V1 does not define equality or hashing for Type.Predicate. Tests and diagnostics should inspect the predicate boolean value and deterministic fact metadata instead.
Fact Preservation¶
Facts are preserved while the static type remains Type.Predicate:
const p = T.implements(PartialEq(T)) // Type.Predicate; facts preserved
const b: bool = p // bool; facts discarded
Facts survive storage in consts, comptime parameters, and return values:
fn comparable(comptime T: Type) Type.Predicate {
return T.implements(PartialEq(T))
}
Facts are discarded when a predicate coerces to bool or is wrapped in a type that does not preserve predicate identity.
Type.Predicate is valid only in comptime positions: comptime constants, comptime parameters, and return values of comptime-evaluated functions. It is invalid in runtime layout, runtime fields, arrays, ordinary runtime parameters, or runtime returns.
Narrowing¶
If a Type.Predicate controls a comptime if condition or declaration guard without first being coerced to bool, sema may apply its true facts to the controlled branch or guarded declaration body.
Facts can narrow stable comptime Type bindings and parameters:
fn maybe_contains(comptime T: Type, comptime S: Sequence(T), xs: *const S, value: *const T) bool {
if T.implements(PartialEq(T)) {
return xs.contains(value)
}
return false
}
Facts do not attach to arbitrary re-evaluated expressions. Bind the type first if a branch needs narrowing.
Narrowing facts are lexical and branch-local:
iffacts apply only in the true branch.- declaration guard facts apply only to that declaration body or surface.
elsereceives no negative fact in V1.- facts do not leak after the branch.
Declaration guards accept comptime-known bool or Type.Predicate. Boolean guards decide availability but do not add facts.
Boolean Operators¶
For Type.Predicate values:
p and qshort-circuits.- the RHS of
qis checked under facts fromp. - the true branch of
p and qreceives facts from both predicates. p or qhas boolean behavior but does not add positive facts in V1.not phas boolean behavior but does not add negative facts in V1.- mixed
Type.Predicateandboolexpressions are allowed, but only fact-bearing predicate subexpressions connected byandcontribute facts.
Boundaries¶
Deferred:
- negative facts
orandnotfact reasoning- enum-union or error-set narrowing
- runtime type/pattern facts from
is - layout, realtime, allocation, and aliasing facts
- equality, ordering, or hashing for
Type.Predicate - runtime representation, formatting, serialization, or ABI for predicates
Comptime diagnostics may format predicates for human-readable messages, but this is not runtime reflection or stable serialization.