E08272: a for-range creates new values of its declared type - its counter as T(start) and each loop variable as T(counter) - so the range type must be concrete and have a public copy constructor T(T) that produces an independent value
What went wrong
A range creates new values of its declared type. So the range type must be CONCRETE and have a public COPY CONSTRUCTOR. A for-range over a type needs that type's COPY CONSTRUCTOR - a public constructor taking exactly one argument of the same type, 'Step(Step)'. The loop makes its own counter as a copy of the start, 'Step(start)', advances that counter in place ('++', '--', '+=' all mutate their receiver), and gives each iteration a NEW loop variable copied from the counter, 'Step(counter)'. The same holds for the expression form ('total <- for i in a ... b') and for a range used as a stream source.
The values are always of the DECLARED type of the range, whatever the start and end objects really are. 'first as BaseStep: TenStep(1)' ranges over BaseStep, so the counter and each loop variable are BaseSteps made by 'BaseStep(BaseStep)' - an open concrete super iterates values of the super. If the declared type is ABSTRACT there are no values of its own to create: 'first as AbstractStep: TenStep(1)' with 'for step in first ... last' is E08272 too (it once compiled and failed at run time with an InstantiationError). Declare the range over the concrete type.
The copy constructor is what makes those values INDEPENDENT. A generated ':=:' is shallow - the copy refers to the same property objects - so if 'Step's '++' mutates an Integer property in place, a shallow copy would move the start, and every loop variable kept from an earlier iteration, along with the counter. Only the author knows what an independent Step is, so the copy constructor decides.
Worked examples
The [EXAMPLE] section below has a 'Step' with '<=>', '++' and '--' but no 'Step(Step)', so 'for step in Step(1) ... Step(3)' is this error. The fix gives Step a copy constructor that makes an independent value:
- Step() as pure
- -> other as Step
- position :=? Integer(other.position)
A start kept in a variable is then still Step(1) after the loop, and a loop variable kept from the first iteration still reads 1.
When the start and end are DECLARED as an abstract type ('first as AbstractStep: TenStep(1)'), the fix is to declare the range over the concrete type instead: 'for step in TenStep(1) ... TenStep(5)'.
A GENERIC that ranges over its type parameter ('for item in first ... last' with 'first as T') calls T's copy constructor too, so the check moves to where the generic is PARAMETERISED - only there is the concrete type known. 'Ranger() of NoCopyStep' is E08272 at that line (POST_RESOLUTION_CHECKS) when NoCopyStep has no public 'NoCopyStep(NoCopyStep)' (missing or private). This holds for a CONSTRAINED type parameter as well ('T constrain by BaseStep'): the parameterising type 'Step' must have its OWN 'Step(Step)'; a 'Step(BaseStep)' (which E06130 requires whenever BaseStep has a copy constructor) is never used in its place - it is the base type's copy rule, not Step's.
The range CALLS the copy constructor, so in a pure scope it must be pure, like any call: E08130 on a concrete range type, E10032 at the parameterisation of a generic that ranges over T in a pure scope.
How to fix it
- Add a public copy constructor - one parameter of exactly the range type - to the range type; if the declared type is abstract, declare the start and end as the concrete type instead.
- Copy each property the range operators mutate as a NEW value ('Integer(other.position)'), not the same object (assigning 'other.position' itself would share it).
- Built-in range types (Integer, Float, Character, Date, DateTime, Time, Duration, Millisecond, Money, Dimension, String, ...) already have one.
Common causes
- A user range type written with only a no-argument and a value constructor
- A copy constructor that is private, or that takes a wider type (a trait, a super, Any)
- Ranging over a trait or abstract type - a trait has no constructors at all, and an abstract type cannot be constructed even when it has a copy constructor
- Declaring the start as an abstract super ('first as Shape: Square(1)') - the range is over the declared type, not the object's
- A generic ranging over 'T' (constrained or not), parameterised with a type that has no public copy constructor of its own - a 'Step(BaseStep)' does not count
See also
- E08272: a range type that is abstract or has no copy constructor (this error)
- E10030: a generic ranging over T, parameterised with an abstract type
- E07620: an operator the range needs ('<=>', '++', '--', '+=' for a 'by' step) is not defined
- E07845: the type cannot be ranged over at all
- E06131: a constrained type's base must be copyable too
- E06130: a constrained T's parameterising type needs the constraining type's constructors
- E08130 / E10032: the copy constructor a range uses in a pure scope must be pure
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E08272: a for-range creates new values of its declared type - its counter as T(start) and each loop variable as T(counter) - so the range type must be concrete and have a public copy constructor T(T) that produces an independent value
#!ek9 defines module error.e08272.bad defines class Step position as Integer? default private Step() as pure Step() as pure -> initial as Integer position :=? Integer(initial) operator <=> as pure -> other as Step <- rtn as Integer: position <=> other.position default operator ? operator $ as pure <- rtn as String: $position operator #? as pure <- rtn as Integer: #?position operator ++ position++ operator -- position-- defines function countSteps() stdout <- Stdout() for step in Step(1) ... Step(3) stdout.println($step)
Fixed: Give the type a public copy constructor T(T)
#!ek9 defines module error.e08272.good defines class Step position as Integer? default private Step() as pure Step() as pure -> initial as Integer position :=? Integer(initial) Step() as pure -> other as Step position :=? Integer(other.position) operator <=> as pure -> other as Step <- rtn as Integer: position <=> other.position default operator ? operator $ as pure <- rtn as String: $position operator #? as pure <- rtn as Integer: #?position operator ++ position++ operator -- position-- defines function countSteps() stdout <- Stdout() for step in Step(1) ... Step(3) stdout.println($step)