Raku / old-issue-tracker

Tickets from RT
https://github.com/Raku/old-issue-tracker/issues
2 stars 1 forks source link

coercions don't ensure result type #5567

Open p6rt opened 8 years ago

p6rt commented 8 years ago

Migrated from rt.perl.org#128927 (status was 'open')

Searchable as RT128927$

p6rt commented 8 years ago

From zefram@fysh.org

The description of coercion in S02 says​:

# The type outside the parens indicates the desired end result, and # subsequent code may depend on it being that type.

Rakudo does not actually ensure that the result of a coercion is of the requested type. We can readily see this by constructing an object with an uncooperative coercion method​:

(sub (Int() $a) { $a })("abc" but role { method Int () { :xyz } }).perl :xyz

Here the sub's $a is supposedly guaranteed to be an Int, but it's actually a Pair. This doesn't only arise from code deliberately constructed to do this. It also arises from some reasonable use of the built-in numeric types, among which there are a couple of uncooperative coercion methods​:

(sub (FatRat() $a) { $a })(Inf).^isa(FatRat) 0

The value received in $a here is apparently a Rational[Num,Int], and it fails to .perl. It's definitely not the promised FatRat.

Because coercion methods are identified by the name of the target type, rather than by some first-class reference to the type, and because scoping allows the same name to refer to different types in different places, there's an inherent possibility that a coercion method written with cooperative intent might be invoked for coercion to the wrong type. Similarly, because coercion methods aren't formally distinguished from other named methods, it's inherently possible for a method never intended for coercion to be invoked for a coercion. Both of those situations look like an uncooperative coercion method. So it is not possible to resolve this problem by putting the onus entirely on coercion methods to return the right type. There has to be enforcement.

-zefram

p6rt commented 7 years ago

From @skids

On Sun, 14 Aug 2016 08​:01​:48 -0700, zefram@​fysh.org wrote​:

The description of coercion in S02 says​:

# The type outside the parens indicates the desired end result, and # subsequent code may depend on it being that type.

Rakudo does not actually ensure that the result of a coercion is of the requested type. We can readily see this by constructing an object with an uncooperative coercion method​:

(sub (Int() $a) { $a })("abc" but role { method Int () { :xyz } }).perl :xyz

Here the sub's $a is supposedly guaranteed to be an Int, but it's actually a Pair. This doesn't only arise from code deliberately constructed to do this. It also arises from some reasonable use of the built-in numeric types, among which there are a couple of uncooperative coercion methods​:

(sub (FatRat() $a) { $a })(Inf).^isa(FatRat) 0

The value received in $a here is apparently a Rational[Num,Int], and it fails to .perl. It's definitely not the promised FatRat.

Because coercion methods are identified by the name of the target type, rather than by some first-class reference to the type, and because scoping allows the same name to refer to different types in different places, there's an inherent possibility that a coercion method written with cooperative intent might be invoked for coercion to the wrong type. Similarly, because coercion methods aren't formally distinguished from other named methods, it's inherently possible for a method never intended for coercion to be invoked for a coercion. Both of those situations look like an uncooperative coercion method. So it is not possible to resolve this problem by putting the onus entirely on coercion methods to return the right type. There has to be enforcement.

-zefram

Just a thought​: we cannot avoid runtime checks when the argument can be anything, but if we know its type at compile time, and if we found and introspected the coercion method, and if that method defined a return type, we might be able to catch errors ahead of time. (We'd still have to deal with Nil/Failure at runtime, though.)

p6rt commented 7 years ago

The RT System itself - Status changed from 'new' to 'open'

usev6 commented 3 years ago

The examples from the bug report are working now:

$ ./rakudo-m -e 'say (sub (Int() $a) { $a })("abc" but role { method Int () { :xyz } }).raku'
Impossible coercion from 'Str+{<anon|1>}' into 'Int': method Int returned an instance of Pair
  in sub  at -e line 1
  in block <unit> at -e line 1
$ ./rakudo-m -e 'say (sub (FatRat() $a) { $a })(Inf).^isa(FatRat)'
1

If I'm not mistaken coercions do ensure the result type now. This has been fixed by @vrurg in the context of https://github.com/Raku/problem-solving/issues/227.

Again if I'm not mistaken there is already a test in roast for (a variation of) the first example. It has been unfudged with https://github.com/Raku/roast/commit/4718d3e1ef#diff-0068e381348d099424e5f745766eed92d86f3a80fa42025cb5247e052e348238L38-R38 . I'd expect that there is a test for (a variation of) the second example, too -- but I didn't see it at a cursory glance. Would require a closer look with a fresh brain.

Nevertheless I'd think it make sense to add the above examples as regression tests somewhere in S12-coercion. Tagging testneeded.