@@ -158,16 +158,6 @@ def f(a : Float) : Float
158158 return a + 1.0
159159end
160160puts(f(2))
161- ` ,
162- shouldError : false ,
163- },
164- {
165- name : "Integer literal to string param is permitted" ,
166- source : `
167- def f(a : String) : String
168- return a
169- end
170- puts(f(42))
171161` ,
172162 shouldError : false ,
173163 },
180170puts(f(nil))
181171puts(f("hi"))
182172puts(f([1, 2]))
183- ` ,
184- shouldError : false ,
185- },
186- {
187- name : "anything to bool-annotated param is permitted" ,
188- source : `
189- def f(a : Bool) : Bool
190- return a
191- end
192- puts(f(0))
193- puts(f("hi"))
194- puts(f([1, 2]))
195173` ,
196174 shouldError : false ,
197175 },
@@ -384,6 +362,192 @@ puts(task.value)
384362 wantSubstr : "cannot pass String literal as argument 1 to 'square'" ,
385363 shouldError : true ,
386364 },
365+ // ---------------------------------------------------------------
366+ // Strict call-site rule: String / Bool / Nil params no longer
367+ // accept arbitrary types (matches the strict variable-annotation
368+ // rule). Numeric carve-out preserved (Int↔Float, Bool→numeric).
369+ // ---------------------------------------------------------------
370+ {
371+ name : "Integer literal to string param is flagged (strict)" ,
372+ source : `
373+ def f(a : String) : String
374+ return a
375+ end
376+ puts(f(42))
377+ ` ,
378+ wantSubstr : "cannot pass Integer literal as argument 1 to 'f' (parameter 'a' declared as String)" ,
379+ shouldError : true ,
380+ },
381+ {
382+ name : "Float literal to string param is flagged (strict)" ,
383+ source : `
384+ def f(a : String) : String
385+ return a
386+ end
387+ puts(f(3.14))
388+ ` ,
389+ wantSubstr : "cannot pass Float literal as argument 1 to 'f' (parameter 'a' declared as String)" ,
390+ shouldError : true ,
391+ },
392+ {
393+ name : "Bool literal to string param is flagged (strict)" ,
394+ source : `
395+ def f(a : String) : String
396+ return a
397+ end
398+ puts(f(true))
399+ ` ,
400+ wantSubstr : "cannot pass Bool literal as argument 1 to 'f' (parameter 'a' declared as String)" ,
401+ shouldError : true ,
402+ },
403+ {
404+ name : "Nil literal to string param is flagged (strict)" ,
405+ source : `
406+ def f(a : String) : String
407+ return a
408+ end
409+ puts(f(nil))
410+ ` ,
411+ wantSubstr : "cannot pass Nil literal as argument 1 to 'f' (parameter 'a' declared as String)" ,
412+ shouldError : true ,
413+ },
414+ {
415+ name : "Array literal to string param is flagged (strict)" ,
416+ source : `
417+ def f(a : String) : String
418+ return a
419+ end
420+ puts(f([1, 2]))
421+ ` ,
422+ wantSubstr : "cannot pass Array literal as argument 1 to 'f' (parameter 'a' declared as String)" ,
423+ shouldError : true ,
424+ },
425+ {
426+ name : "String literal to bool param is flagged (strict)" ,
427+ source : `
428+ def f(a : Bool) : Bool
429+ return a
430+ end
431+ puts(f("hi"))
432+ ` ,
433+ wantSubstr : "cannot pass String literal as argument 1 to 'f' (parameter 'a' declared as Bool)" ,
434+ shouldError : true ,
435+ },
436+ {
437+ name : "Integer literal to bool param is flagged (strict)" ,
438+ source : `
439+ def f(a : Bool) : Bool
440+ return a
441+ end
442+ puts(f(0))
443+ ` ,
444+ wantSubstr : "cannot pass Integer literal as argument 1 to 'f' (parameter 'a' declared as Bool)" ,
445+ shouldError : true ,
446+ },
447+ {
448+ name : "Array literal to bool param is flagged (strict)" ,
449+ source : `
450+ def f(a : Bool) : Bool
451+ return a
452+ end
453+ puts(f([1, 2]))
454+ ` ,
455+ wantSubstr : "cannot pass Array literal as argument 1 to 'f' (parameter 'a' declared as Bool)" ,
456+ shouldError : true ,
457+ },
458+ {
459+ name : "Tier 3: typed Integer variable to string param is flagged" ,
460+ source : `
461+ def f(a : String) : String
462+ return a
463+ end
464+ x : Integer = 42
465+ puts(f(x))
466+ ` ,
467+ wantSubstr : "cannot pass Integer value as argument 1 to 'f' (parameter 'a' declared as String)" ,
468+ shouldError : true ,
469+ },
470+ {
471+ name : "Tier 3: typed String variable to bool param is flagged" ,
472+ source : `
473+ def f(a : Bool) : Bool
474+ return a
475+ end
476+ x : String = "hi"
477+ puts(f(x))
478+ ` ,
479+ wantSubstr : "cannot pass String value as argument 1 to 'f' (parameter 'a' declared as Bool)" ,
480+ shouldError : true ,
481+ },
482+ // ---------------------------------------------------------------
483+ // Numeric carve-out: still permissive at call sites for the
484+ // Integer/Float/Bool numeric family.
485+ // ---------------------------------------------------------------
486+ {
487+ name : "Integer literal to float param is permitted (numeric carve-out)" ,
488+ source : `
489+ def f(a : Float) : Float
490+ return a + 1.0
491+ end
492+ puts(f(2))
493+ ` ,
494+ shouldError : false ,
495+ },
496+ {
497+ name : "Float literal to int param is permitted (numeric carve-out)" ,
498+ source : `
499+ def f(a : Integer) : Integer
500+ return a + 1
501+ end
502+ puts(f(2.5))
503+ ` ,
504+ shouldError : false ,
505+ },
506+ {
507+ name : "Bool literal to int param is permitted (numeric carve-out)" ,
508+ source : `
509+ def f(a : Integer) : Integer
510+ return a + 1
511+ end
512+ puts(f(true))
513+ ` ,
514+ shouldError : false ,
515+ },
516+ // ---------------------------------------------------------------
517+ // Any annot still accepts anything.
518+ // ---------------------------------------------------------------
519+ {
520+ name : "anything to any-annotated param is still permitted" ,
521+ source : `
522+ def f(x : Any) : Any
523+ return x
524+ end
525+ puts(f(nil))
526+ puts(f("hi"))
527+ puts(f([1, 2]))
528+ ` ,
529+ shouldError : false ,
530+ },
531+ {
532+ name : "Same-type matches still pass: String to String" ,
533+ source : `
534+ def f(s : String) : String
535+ return s
536+ end
537+ puts(f("hello"))
538+ ` ,
539+ shouldError : false ,
540+ },
541+ {
542+ name : "Same-type matches still pass: Bool to Bool" ,
543+ source : `
544+ def f(b : Bool) : Bool
545+ return !b
546+ end
547+ puts(f(true))
548+ ` ,
549+ shouldError : false ,
550+ },
387551 }
388552
389553 for _ , tc := range cases {
0 commit comments