@@ -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