What syntactic and structural rules constrain where and how a contract { } block can be declared?
answer
- First statement, block body only
- No expression-body, no lambdas
- Conditions: null checks, is/!is, boolean params
- Only parameters/receiver, not locals
- @OptIn or -opt-in compiler flag
basics
~10 sThe contract block must be the first line inside the function, it can only go on regular named functions with a body, and you must opt in to the experimental API.
solid answer
~40 sThe contract { } call must be the first statement in the function body — the compiler rejects it otherwise. It is only valid on functions with a block body (not expression-body functions, not lambdas/anonymous functions, and historically not on property accessors). The function must opt in via @OptIn(ExperimentalContracts::class) or be in a module that opts in globally, because contract is annotated @ExperimentalContracts. The conditions you reference inside (e.g. value != null) must be 'simple' expressions the contract DSL allows — comparisons to null, is/!is checks, boolean parameters, and true/false constants — not arbitrary logic. You cannot reference local variables, only parameters and the boolean return value via Returns/ReturnsNotNull style effects.
code
kotlin · 5 lines@OptIn(ExperimentalContracts::class)
fun isNonEmpty(s: String?): Boolean {
contract { returns(true) implies (s != null) }
return s != null && s.isNotEmpty()
}go deeper
Remembers the contract must be first and needs opt-in.
Lists where contracts are allowed (block-body named functions) and the restricted condition grammar.
Explains the rationale (whole-function attachment, restricted DSL grammar) and the module-level opt-in flag.
Weighs the grammar restrictions when designing a library of smart-cast helpers and anticipates compiler-version differences in accessor support.
## Placement: first statement only The `contract { }` builder call MUST be the **first statement** of the function body. This is enforced by the compiler — putting any code before it is a compile error. The reason is that contracts describe the whole function and must be unambiguously attached before any logic runs. ```kotlin @OptIn(ExperimentalContracts::class) fun valid(x: Any?) { contract { returns() implies (x is String) } // OK: first statement require(x is String) } @OptIn(ExperimentalContracts::class) fun invalid(x: Any?) { val y = x // some code first contract { /* ... */ } // COMPILE ERROR: not the first statement } ``` ## Where contracts are allowed - Allowed on **top-level functions** and **member functions** with a **block body** `{ }`. - **Not** on expression-body functions (`fun f() = ...`) — there is no place for a leading statement. - **Not** on lambdas or anonymous functions. - Property getters/setters historically could not declare contracts (support is limited/experimental). ## What the condition expressions may contain The DSL only accepts a restricted grammar of "effect expressions": - `==` / `!=` comparisons against `null` (e.g. `param != null`). - `is` / `!is` type checks on a **parameter** or the receiver. - A boolean **parameter** by itself, or its negation `!param`. - The constants `true` / `false`. You reference only the function's **parameters** (and receiver), never arbitrary local variables, and you combine them with the contract operators `returns(...)`, `implies`, and `callsInPlace(...)` — not with normal `if`/`&&`/`||` logic mixed freely. ## Opt-in requirement `contract` carries `@ExperimentalContracts`, so each declaring function needs `@OptIn(ExperimentalContracts::class)`, or the module must opt in via the compiler flag `-opt-in=kotlin.contracts.ExperimentalContracts`. ## Summary checklist - First statement. - Block-body, named function. - Opt-in present. - Conditions reference parameters/receiver with the allowed comparison/type-check grammar.
- Can you put a contract on an expression-body function like fun f() = ...?No — there is no leading statement slot. Convert it to a block body { } if you need a contract.
- Can a contract condition reference a local variable computed inside the function?No. Conditions may only reference parameters and the receiver, using the allowed null/type-check/boolean grammar.
saying these in an interview costs you the question
- Claiming any statement order is fine
- Trying to add a contract to a lambda
- Referencing local variables in the contract condition
- Using arbitrary && / || logic inside implies
- Forgetting the opt-in and assuming it just works