Status: fixed in 61693ce on PR #65. This body was corrected after review;
see the comment below for the implemented fix, which differs from the approach
originally suggested here.
Summary
directParamValue builds the hidden alias select chain over every output of a
function. When a range-bearing callee has a direct scalar parameter and a sibling
output that lowers to a different LLVM type, the chain emits a select between
mismatched types and then an arithmetic op on the wrong type.
The program compiles and returns a wrong answer. LLVM's in-memory verifier does
reject the function (Function is broken), but the compiler does not run
verification, so nothing surfaces it — see #68 for why verification is currently
unavailable. Depending on the shape it can also abort object emission rather than
merely return a wrong value.
Reproduce
lib.pt
sum, half = M2(a, x)
sum = a + x
half = x * 0.5
t.spt
s = 1
h = 0.0
s, h = M2(s, 0:4)
"S: -s H: -h"
Expected S: 7 H: 1.5 (s accumulates 1→1→2→4→7). Actual:
Emitted IR
From pluto -emit-ir, inside @Pt_3isa_p_2M2_f2_I64_Range_t1_I64:
%a_alias_value_0 = select i1 %a_alias_match_0, i64 %sum_alias_load_0, i64 %1
%a_alias_value_1 = select i1 %a_alias_match_1, double %half_alias_load_1, i64 %a_alias_value_0
%add_tmp = add double %a_alias_value_1, i64 %iter
The second select mixes double and i64, and the following add inherits it.
Cause
processParams passes the function's complete output-name list to
bindParamAlias, and directParamValue folds each of those outputs into a select
against the scalar parameter value. coerceSymbolForType only bridges Str/Array/Table
flavors; with an Int/Float target it returns the output's own type unchanged, so
the operand types diverge.
Trigger conditions
- the variant has at least one
Range/ArrayRange parameter, so direct scalars get alias slots
- at least one direct
I64/F64 parameter is actually read in the callee body
- at least one output lowers to a different LLVM type than that parameter
A single mismatched output is enough, and actual caller aliasing is not required:
selector zero still emits the invalid select. A Str or Array sibling output
reproduces it too.
Scope
Pre-existing. Reproduces identically on 9fca83a and on the branch tip before the
fix, so it is not a regression from any recent PR.
Summary
directParamValuebuilds the hidden alias select chain over every output of afunction. When a range-bearing callee has a direct scalar parameter and a sibling
output that lowers to a different LLVM type, the chain emits a
selectbetweenmismatched types and then an arithmetic op on the wrong type.
The program compiles and returns a wrong answer. LLVM's in-memory verifier does
reject the function (
Function is broken), but the compiler does not runverification, so nothing surfaces it — see #68 for why verification is currently
unavailable. Depending on the shape it can also abort object emission rather than
merely return a wrong value.
Reproduce
lib.ptt.sptExpected
S: 7 H: 1.5(s accumulates 1→1→2→4→7). Actual:Emitted IR
From
pluto -emit-ir, inside@Pt_3isa_p_2M2_f2_I64_Range_t1_I64:The second
selectmixesdoubleandi64, and the followingaddinherits it.Cause
processParamspasses the function's complete output-name list tobindParamAlias, anddirectParamValuefolds each of those outputs into aselectagainst the scalar parameter value.
coerceSymbolForTypeonly bridges Str/Array/Tableflavors; with an
Int/Floattarget it returns the output's own type unchanged, sothe operand types diverge.
Trigger conditions
Range/ArrayRangeparameter, so direct scalars get alias slotsI64/F64parameter is actually read in the callee bodyA single mismatched output is enough, and actual caller aliasing is not required:
selector zero still emits the invalid select. A
StrorArraysibling outputreproduces it too.
Scope
Pre-existing. Reproduces identically on
9fca83aand on the branch tip before thefix, so it is not a regression from any recent PR.