Skip to content

Commit d77b3e1

Browse files
author
ab5tract
committed
[RakuAST] Fix parameterizations of types in is trait
Considering role R[::T] { has @.a is Array[T] } my $r = R[Int].new: :a(1,2,3) # Illegal binding exception, "expected `T` and got `Int`" This would previously fail because the type capture `T` was "frozen" into the parameterization of `@.a`. This patch fixes the above issue, which only affected types used with the `is` trait. Please see comments inline for more details, but essentially what we do is add some significant fiddling around when a parameterized type is passed to `is`. We do this by setting an explicit container base type and also setting a specialized `$!key-type` attribute to `RakuAST::VarDeclaration::Simple` that is then used in parameterizations. Note that `RakuAST::VarDeclaration::Simple` is probably due for a bit of untangling, but it can wait for now. R#5970 (#5970)
1 parent 507bbf5 commit d77b3e1

File tree

1 file changed

+53
-8
lines changed

1 file changed

+53
-8
lines changed

src/Raku/ast/variable-declaration.rakumod

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ class RakuAST::ContainerCreator {
209209
}
210210
}
211211
else {
212+
# $of is derived from self.type, and self.type is a RakuAST node,
213+
# which is cleaner to check for truthiness than the equivalent !($of =:= Mu).
212214
if self.type {
213215
$container-type := Hash.HOW.parameterize(Hash, $of, $key-type);
214216
$bind-constraint := $bind-constraint.HOW.parameterize(
@@ -238,9 +240,19 @@ class RakuAST::ContainerCreator {
238240
}
239241
}
240242
else {
241-
$container-type := self.type
242-
?? $!explicit-container-base-type.HOW.parameterize($!explicit-container-base-type, $of)
243-
!! $!explicit-container-base-type;
243+
# The following logic cascade is in charge of assuring user's who have specified
244+
# an, eg. `my @h is Array[::T]` will have their containers parameterized properly,
245+
# in a way where the capture will be successfully resolved.
246+
my str $sigil := self.sigil;
247+
$container-base-type := $!explicit-container-base-type;
248+
$container-type := self.type || ! ($key-type =:= NQPMu)
249+
?? $sigil eq '%' && !($key-type =:= NQPMu)
250+
?? $container-base-type =:= Hash || nqp::eqaddr($container-base-type,self.IMPL-SIGIL-TYPE)
251+
?? $container-base-type.HOW.parameterize($container-base-type, $of, $key-type)
252+
!! $container-base-type.HOW.parameterize($container-base-type, $key-type)
253+
!! $container-base-type.HOW.parameterize($container-base-type, $of)
254+
!! $container-base-type;
255+
$bind-constraint := $container-type;
244256
}
245257

246258
nqp::bindattr(self, RakuAST::ContainerCreator, '$!initialized', True);
@@ -600,6 +612,7 @@ class RakuAST::VarDeclaration::Simple
600612
has Bool $!is-bindable;
601613
has Bool $!already-declared;
602614
has RakuAST::Code $!block;
615+
has Mu $!key-type;
603616

604617
has Mu $!container-initializer;
605618
has Mu $!package;
@@ -644,6 +657,8 @@ class RakuAST::VarDeclaration::Simple
644657
$where // RakuAST::Expression);
645658
nqp::bindattr($obj, RakuAST::VarDeclaration::Simple, '$!original-type',
646659
$type // RakuAST::Type);
660+
nqp::bindattr($obj, RakuAST::VarDeclaration::Simple, '$!key-type',
661+
Mu); # Mu is the default key type of all hashes and hash-like things
647662
nqp::bindattr($obj, RakuAST::VarDeclaration::Simple, '$!is-rw', False);
648663
nqp::bindattr($obj, RakuAST::VarDeclaration::Simple, '$!is-ro', False);
649664
nqp::bindattr($obj, RakuAST::VarDeclaration::Simple, '$!is-bindable', True);
@@ -792,9 +807,15 @@ class RakuAST::VarDeclaration::Simple
792807

793808
method IMPL-CALCULATE-TYPES(Mu $of) {
794809
my &calculate-types := nqp::findmethod(RakuAST::ContainerCreator, 'IMPL-CALCULATE-TYPES');
795-
$!shape && self.sigil eq '%'
796-
?? &calculate-types(self, $of, :key-type($!shape.code-statements[0].expression.compile-time-value))
797-
!! &calculate-types(self, $of);
810+
if self.sigil eq '%' {
811+
nqp::isconcrete($!shape)
812+
?? &calculate-types(self, $of, :key-type($!shape.code-statements[0].expression.compile-time-value))
813+
!! $!key-type =:= Mu # no key type is defined
814+
?? &calculate-types(self, $of)
815+
!! &calculate-types(self, $of, :key-type($!key-type))
816+
} else {
817+
&calculate-types(self, $of);
818+
}
798819
}
799820

800821
# Runs before we parse the initializer, so we can setup a proper environment for resolving
@@ -883,9 +904,33 @@ class RakuAST::VarDeclaration::Simple
883904

884905
# an actual type
885906
if nqp::isconcrete($type) && !$_.argument
886-
&& (!nqp::istype($type, RakuAST::Lookup) || $type.is-resolved)
907+
&& (!nqp::istype($type, RakuAST::Lookup) || $type.is-resolved)
887908
{
888-
self.IMPL-SET-EXPLICIT-CONTAINER-BASE-TYPE($type.meta-object);
909+
if nqp::istype($type,RakuAST::Type::Parameterized) {
910+
# Only @- and %-sigiled variables can take an `is $type` trait.
911+
# Otherwise we would want to guard to non-$-sigiled variables.
912+
# Note that the check below for number of arguments feels very fiddly.
913+
# It does seem to cover the majority of uses but the larger issue
914+
# Without setting the type here, a type capture fails to resolve to their
915+
# parameterization.
916+
my @of-types := self.IMPL-UNWRAP-LIST($type.args.args);
917+
my $num-types := nqp::elems(@of-types);
918+
# TODO: Throw an exception instead. Also, is it theoretically possible for a user to have
919+
# TODO: more than two types in their parameterization?
920+
nqp::die("Unable to parameterize a " ~ self.sigil ~ " container with " ~ $num-types ~ " type parameters")
921+
unless $num-types <= 2;
922+
my $bt-mo := $type.base-type.meta-object;
923+
if self.sigil eq '%' {
924+
self.IMPL-SET-EXPLICIT-CONTAINER-BASE-TYPE: $bt-mo;
925+
nqp::bindattr(self,RakuAST::VarDeclaration::Simple,'$!key-type',@of-types[0].meta-object);
926+
# for the cases where Associative[Key,OfType] (as opposed to QuantHash[Key])
927+
self.set-type(@of-types[1]) if $num-types == 2;
928+
} else {
929+
self.set-type(@of-types[0]);
930+
}
931+
} else {
932+
self.IMPL-SET-EXPLICIT-CONTAINER-BASE-TYPE($type.meta-object);
933+
}
889934
next;
890935
}
891936
}

0 commit comments

Comments
 (0)