E08276: a for-range whose start, end and step are known when the program is compiled can never step from its start to its end - its step is zero, moves away from the end, or takes a Time range across midnight; give 'by' a step that moves from the start toward the end (negative to count down), or leave 'by' out

Category: E08xxx Pre-IR checks (flow analysis) · Reported in phase: PRE_IR_CHECKS · Classification: RANGE_NEVER_PROGRESSES · All error codes

What went wrong

A 'for' range gives its start, its end and a 'by' step, and the compiler knows all three while it compiles: they are literals, constants, a local that was only given those values, or a constrained value such as Age(10) built from one. The step can never move the counter from the start toward the end - it is zero, or it counts the wrong way - so the loop body can never run. At run time such a loop does nothing at all, without a word, which is never what the program meant. The same holds when the range is the source of a stream. Each of these is E08276:

//The step is zero: the counter never moves.
for count in 1 ... 9 by NO_STEP
  stdout.println($count)

//Counting up, but the range runs down.
for count in 9 ... 1 by 2
  stdout.println($count)

//Counting down, but the range runs up.
for price in 1.00#GBP ... 3.00#GBP by -0.50#GBP
  stdout.println($price)

//A Time range never crosses midnight: 23:50 plus ten minutes is 00:00, before the end.
for clock in Time(23:40) ... Time(23:55) by PT10M
  stdout.println($clock)

A Time range never crosses midnight. A Time step wraps round the clock (23:50 plus ten minutes is 00:00), so 'Time(23:40) ... Time(23:55) by PT10M' would step past midnight before it reached its end and go round again, and an overnight range such as 'Time(23:00) ... Time(01:00) by PT30M' has a start later in the day than its end, so counting up can never reach it. A step of a day or more, or one that passes 00:00 on its first move ('08:00 ... 09:00 by PT-23H'), crosses midnight too. All are E08276. Write the day as two ranges

  • one from the start to the end of the day, one from 00:00 to the end - or use a DateTime range, which

does cross midnight.

A range whose start equals its end runs once whatever the step, and a step larger than the span runs once too, so neither is reported. A step, start or end that is known only while the program runs (a parameter, a call's result, a property) is never reported: the compiler only rejects what it can prove. Inside the body of another loop a variable is not read at all - a later pass may give it a different value - so there only literals and constants are checked. A Duration range is read like a number; Dates, DateTimes, Milliseconds, Characters, enumerations, Durations written in weeks, months or years, and ranges over your own types are not read by this check, and neither are Money or Dimension values that carry different currencies or units.

Worked examples

Each of these is fine:

  • //Count up with a positive step.
  • for count in 1 ... 9 by 2
  • stdout.println($count)
  • //Count down with a negative step, or leave 'by' out: the direction comes from the start and end.
  • for count in 9 ... 1 by -2
  • stdout.println($count)
  • for count in 9 ... 1
  • stdout.println($count)
  • //A step known only while the program runs is never reported.
  • for count in 1 ... 9 by stepSize
  • stdout.println($count)
  • //Cross midnight with two Time ranges, one to the end of the day and one from 00:00.
  • for clock in Time(23:40) ... Time(23:50) by PT10M
  • stdout.println($clock)
  • for clock in Time(00:00) ... Time(00:20) by PT10M
  • stdout.println($clock)

How to fix it

  1. To count up give a positive step ('by 2'); to count down give a negative one ('by -2').
  2. Or leave 'by' out: the direction comes from the start and the end, and the step is one.
  3. If the step is meant to be configurable, take it from a parameter or a call - a step that is only known while the program runs is not reported, and a range it cannot move along runs no iterations.

Common causes

  • Swapping the start and end of a range and forgetting to change the sign of the step
  • A constant for the step that was left at its starting value of zero
  • Porting a loop from a language where the step's sign is read from the bounds

See also

  • E08260: a constrained value, such as an Age bound, that is known to break its constraint
  • E08272: a range over a type with no public copy constructor

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E08276: a for-range whose start, end and step are known when the program is compiled can never step from its start to its end - its step is zero, moves away from the end, or takes a Time range across midnight; give 'by' a step that moves from the start toward the end (negative to count down), or leave 'by' out

#!ek9
defines module error.e08276.bad

  defines constant
    NO_STEP <- 0

  defines function

    rangesThatNeverRun()
      stdout <- Stdout()
      //The step is zero: the counter never moves.
      for count in 1 ... 9 by NO_STEP
        stdout.println($count)

      //Counting up, but the range runs down.
      for count in 9 ... 1 by 2
        stdout.println($count)

      //Counting down, but the range runs up.
      for price in 1.00#GBP ... 3.00#GBP by -0.50#GBP
        stdout.println($price)

      //A Time range never crosses midnight: 23:50 plus ten minutes is 00:00, before the end.
      for clock in Time(23:40) ... Time(23:55) by PT10M
        stdout.println($clock)

Fixed: Give by a step that moves from the start toward the end, or leave by out

#!ek9
defines module error.e08276.good

  defines function

    rangesThatRun()
      -> stepSize as Integer
      stdout <- Stdout()
      //Count up with a positive step.
      for count in 1 ... 9 by 2
        stdout.println($count)

      //Count down with a negative step, or leave 'by' out: the direction comes from the start and end.
      for count in 9 ... 1 by -2
        stdout.println($count)

      for count in 9 ... 1
        stdout.println($count)

      //A step known only while the program runs is never reported.
      for count in 1 ... 9 by stepSize
        stdout.println($count)

      //Cross midnight with two Time ranges, one to the end of the day and one from 00:00.
      for clock in Time(23:40) ... Time(23:50) by PT10M
        stdout.println($clock)

      for clock in Time(00:00) ... Time(00:20) by PT10M
        stdout.println($clock)

Questions and answers

These Q&A entries show E08276 and its fix: