For loops should always assume indexer is non-nil for strict typechecking

Reproduction Steps

Copy/paste this script into a blank place:

Source Code
--!strict

local function Counter(maxValue: number): ((invariant: any, index: number) -> (number?), any, number)
	return function(invariant: any, index: number)
		if index + 1 <= maxValue then
			return index + 1;
		else
			--Signal end of iteration.
			return nil;
		end
	end, nil, 0;
end

for value in Counter(5) do
	print(value * 2);
	--> 2
	--> 4
	--> 6
	--> 8
	--> 10
end

Notice that the type checker is unhappy.

Expected Behavior

I expect there to be no type errors.

Actual Behavior

There is a type error.

You can see it’s unhappy because the iterator returns a number?. However, the contract for a for loop is that if the first returned value is nil, the for loop should terminate. Thus, we will never have value == nil inside of the for loop body, and so the type of value should be considered number, NOT number?

Workaround

You can put an assertion on the first line of the for loop.

assert(value ~= nil);

This adds a bit of runtime cost for something the language already guarantees, so that’s a bit lame.

You can also go HAM on any-casting. If you make the return type of my Counter iterator number and just cast nil to any, you can circumvent this issue.

Issue Area: Studio
Issue Type: Other
Impact: Moderate
Frequency: Rarely

6 Likes

Thanks for the report! We’ll investigate.

2 Likes

This seems to be fixed. Could you please try to repro with the latest release to confirm? Thank you.