E07930: cannot assign from sanitized parameter - creates hidden copy semantics
What went wrong
Direct assignment FROM a sanitized parameter creates hidden aliasing. When you write 'local <- sanitizedParam', both variables point to the SAME sanitized copy - mutations to one affect the other.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 662 (How do I safely work with sanitized parameters without al...)
- ek9 -q 686 (Must an override keep the 'sanitized' modifier of its par...)
- ek9 -q 665 (How do sanitized parameters work with web service content...)
Rationale
A 'sanitized' parameter is the receiving function's own cleaned copy: on entry the function replaces it with a copy (or an unset String for a threat), so the caller's String is never shared. Direct assignment then creates hidden aliasing of that copy: after 'alias <- data', both names refer to the SAME String, so 'alias += " mod"' also changes 'data'.
This defeats the purpose of defensive copying and creates confusing aliasing semantics that are invisible at the code level.
Guards in control flow
Guards in if, switch, and other control flow statements also create aliases: a declaration guard ('if local <- input', 'switch local <- input') and an assignment pre-flow ('if local := input then local?') each make 'local' the same String as the sanitized parameter. The [EXAMPLE] section below has every one of these forms, each failing with this error, and the fixes.
Pure context constraints
In a 'pure' context, :=: and :~: are NOT available (they mutate). Your options are limited to:
- New declaration of a copy: local <- String(input)
- Direct use without assignment
- Pass to another pure function
- String concatenation (creates new string)
How to fix it
- USE: local <- String(input) - works in ALL contexts including pure
- USE: Direct use, pass to function, or concatenation
- NON-PURE ONLY: :=: or :~: on initialized variables
See also
- E07920: sanitized in wrong location
- E07930: assignment from sanitized (this error)
- E07940: sanitized dropped in an override
https://ek9.io/sanitized.html https://ek9.io/forAI.json#$.sanitizedTypes
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E07930: cannot assign from sanitized parameter - creates hidden copy semantics
#!ek9 defines module error.e07930.bad defines function badDeclarationFromSanitized() -> input as sanitized String <- result as String? //ERROR: Direct declaration assignment from sanitized parameter local <- input //Use local to avoid NOT_REFERENCED error result: local badReassignmentFromSanitized() -> input as sanitized String <- result as String? local <- "initial" //ERROR: Reassignment from sanitized parameter local := input result: local badGuardedAssignmentFromSanitized() -> input as sanitized String <- result as String? local <- String() //ERROR: Assign-if-unset from sanitized parameter local :=? input result: local okDirectUse() -> input as sanitized String <- result as String? //OK: Using sanitized parameter directly is fine result: input okExplicitCopy() -> input as sanitized String <- result as String? //OK: Explicit copy makes semantics clear local <- String(input) result: local okPassToAnotherFunction() -> input as sanitized String <- result as String? //OK: Passing to another function is fine result: processData(input) processData() -> data as String <- result as String? result: data // ... operator-variants <?- BAD: Guard in if statement could create alias to sanitized parameter. The guard uses '<-' which is an assignment. -?> badGuardInIfFromSanitized() -> input as sanitized String <- result as String? //ERROR: Guard creates alias to sanitized parameter if local <- input result: local else result: "empty" <?- BAD: Guard in switch statement could create alias to sanitized parameter. -?> badGuardInSwitchFromSanitized() -> input as sanitized String <- result as String? //ERROR: Guard creates alias to sanitized parameter switch local <- input case "test" result: local default result: "other" <?- BAD: An assignment pre-flow (:=) could create alias to sanitized parameter. The ':=' assigns to an EXISTING variable; 'then local?' then gates on it being set. -?> badGuardExpressionFromSanitized() -> input as sanitized String <- result as String? //Declare local first (required for the ':=' pre-flow) local <- String() //ERROR: Guard creates alias to sanitized parameter if local := input then local? result: local else result: "empty"
Fixed: Copy it, or use it directly
#!ek9 defines module error.e07930.good defines function //A new String made from the sanitized one process() as pure -> input as sanitized String <- rtn as String? localCopy <- String(input) rtn: "Processed: " + localCopy //Use it directly in an expression query() as pure -> input as sanitized String <- rtn as String: "Query: " + input
Fixed: Outside pure code, copy or merge into an initialised variable
#!ek9 defines module error.e07930.good.nonpure defines function //Copy INTO an initialised variable useCopyOperator() -> input as sanitized String <- rtn as String: String() rtn :=: String(input) //Merge INTO an initialised variable useMergeOperator() -> input as sanitized String <- rtn as String: "Base: " rtn :~: String(input) //Use it directly, or pass it on useDirectly() -> input as sanitized String stdout <- Stdout() stdout.println(input)
Questions and answers
These Q&A entries show E07930 and its fix:
- Q216: How does EK9 detect and report security threats?
- Q217: How do sanitized parameters interact with pure functions in EK9?
- Q662: How do I safely work with sanitized parameters without aliasing?
- Q665: How do sanitized parameters work with web service content binding?
- Q686: Must an override keep the 'sanitized' modifier of its parent?