diff --git a/src/Raku/ast/code.rakumod b/src/Raku/ast/code.rakumod index d1fd1cb38d4..d2bd18a052e 100644 --- a/src/Raku/ast/code.rakumod +++ b/src/Raku/ast/code.rakumod @@ -909,6 +909,7 @@ class RakuAST::Block method clear-attachments() { self.clear-handler-attachments(); + self.clear-used-dynamic-variables(); self.clear-placeholder-attachments(); self.clear-phaser-attachments(); Nil @@ -1350,6 +1351,7 @@ class RakuAST::Routine method clear-attachments() { self.clear-handler-attachments(); + self.clear-used-dynamic-variables(); self.clear-placeholder-attachments(); self.clear-phaser-attachments(); Nil diff --git a/src/Raku/ast/compunit.rakumod b/src/Raku/ast/compunit.rakumod index dea5f6b0d08..39d16ec7b4c 100644 --- a/src/Raku/ast/compunit.rakumod +++ b/src/Raku/ast/compunit.rakumod @@ -158,6 +158,7 @@ class RakuAST::CompUnit nqp::setelems($!init-phasers, 0); nqp::setelems($!end-phasers, 0); self.clear-handler-attachments(); + self.clear-used-dynamic-variables(); Nil } diff --git a/src/Raku/ast/scoping.rakumod b/src/Raku/ast/scoping.rakumod index 6ae05970d71..5ae60764c0f 100644 --- a/src/Raku/ast/scoping.rakumod +++ b/src/Raku/ast/scoping.rakumod @@ -17,6 +17,8 @@ class RakuAST::LexicalScope has Mu $!catch-handlers; has Mu $!control-handlers; + has Hash $!used-dynamic-variables; + method IMPL-QAST-DECLS(RakuAST::IMPL::QASTContext $context) { my $stmts := QAST::Stmts.new(); @@ -237,6 +239,22 @@ class RakuAST::LexicalScope Nil } + method add-used-dynamic-variable(RakuAST::Var::Dynamic $var) { + unless nqp::isconcrete($!used-dynamic-variables) { + nqp::bindattr(self, RakuAST::LexicalScope, '$!used-dynamic-variables', {}); + } + $!used-dynamic-variables{$var.name} := $var; + } + + method clear-used-dynamic-variables() { + nqp::bindattr(self, RakuAST::LexicalScope, '$!used-dynamic-variables', {}); + } + + method uses-dynamic-variable(str $name) { + nqp::isconcrete($!used-dynamic-variables) + && nqp::existskey($!used-dynamic-variables, $name) + } + method IMPL-WRAP-SCOPE-HANDLER-QAST(RakuAST::IMPL::QASTContext $context, Mu $statements, Bool :$is-handler) { # If it's an exception handler, add rethrow logic. diff --git a/src/Raku/ast/variable-access.rakumod b/src/Raku/ast/variable-access.rakumod index 2b85e6908f5..a042e423d52 100644 --- a/src/Raku/ast/variable-access.rakumod +++ b/src/Raku/ast/variable-access.rakumod @@ -88,6 +88,7 @@ class RakuAST::Var::Lexical::Setting class RakuAST::Var::Dynamic is RakuAST::Var is RakuAST::Lookup + is RakuAST::Attaching { has str $.name; @@ -111,6 +112,10 @@ class RakuAST::Var::Dynamic Nil } + method attach(RakuAST::Resolver $resolver) { + $resolver.current-scope.add-used-dynamic-variable(self); + } + method IMPL-EXPR-QAST(RakuAST::IMPL::QASTContext $context) { # If it's resolved in the current scope, just a lexical access. if self.is-resolved { diff --git a/src/Raku/ast/variable-declaration.rakumod b/src/Raku/ast/variable-declaration.rakumod index 89e501cf6c1..bc21c0dc197 100644 --- a/src/Raku/ast/variable-declaration.rakumod +++ b/src/Raku/ast/variable-declaration.rakumod @@ -405,6 +405,7 @@ class RakuAST::VarDeclaration::Simple is RakuAST::Meta is RakuAST::Attaching is RakuAST::BeginTime + is RakuAST::CheckTime is RakuAST::Term is RakuAST::Doc::DeclaratorTarget { @@ -417,6 +418,7 @@ class RakuAST::VarDeclaration::Simple has RakuAST::SemiList $.shape; has RakuAST::Package $!attribute-package; has RakuAST::Method $!accessor; + has Bool $!is-illegal-postdeclaration; has Mu $!container-initializer; has Mu $!package; @@ -532,6 +534,11 @@ class RakuAST::VarDeclaration::Simple nqp::bindattr(self, RakuAST::VarDeclaration::Simple, '$!package', $package); } + + nqp::bindattr(self, RakuAST::VarDeclaration::Simple, '$!is-illegal-postdeclaration', False); + if self.twigil eq '*' && $resolver.current-scope.uses-dynamic-variable(self.name) { + nqp::bindattr(self, RakuAST::VarDeclaration::Simple, '$!is-illegal-postdeclaration', True); + } } method PERFORM-BEGIN(RakuAST::Resolver $resolver, RakuAST::IMPL::QASTContext $context) { @@ -660,6 +667,12 @@ class RakuAST::VarDeclaration::Simple } } + method PERFORM-CHECK(RakuAST::Resolver $resolver, RakuAST::IMPL::QASTContext $context) { + self.add-sorry: $resolver.build-exception: + 'X::Dynamic::Postdeclaration', :symbol(self.name) + if $!is-illegal-postdeclaration; + } + method PRODUCE-IMPLICIT-LOOKUPS() { my @lookups; my str $scope := self.scope;