Skip to content

Commit

Permalink
RakuAST: add sorry for using placeholder when not allowed
Browse files Browse the repository at this point in the history
When a placeholder is encountered, check whether the signature of
the placeholder owner already has an instantiated parameters list.
If it has, then add a X::Signature::Placeholder sorry.  Except if
the placeholder name is '@_' or '%_': if *these* occur in the
parameters list, then they're ok:

    sub (*@_) { @_ }   # valid
    sub (*@A) { @_ }   # *NOT* valid
    sub ()    { @_ }   # *NOT* valid
    sub       { @_ }   # valid

This is achieved by a bit of a refactor on Signature.parameters.
If not specified at creation of the Signature, then it will remain
undefined to indicate no actual parameters are known yet.  The
Signature.parameters-initialized method can be used to check that.

Internally, if defined, the $!parameters attribute now contains a
low-level list, which means that accesses for looping on that will
need an extra check.  But on the other hand, would not need unwrapping.
The Signature.parameters method returns a HLL List as before, and
the parameters argument with .new also assumes a HLL List to be given.

This adds a PERFORM-CHECK to RakuAST::VarDeclaration::Placeholder
that does all of the checking.
  • Loading branch information
lizmat committed Mar 16, 2023
1 parent 9db0bc9 commit 6d9c4c7
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 21 deletions.
63 changes: 42 additions & 21 deletions src/Raku/ast/signature.rakumod
Expand Up @@ -19,7 +19,8 @@ class RakuAST::Signature
method new(List :$parameters, RakuAST::Node :$returns) {
my $obj := nqp::create(self);
nqp::bindattr($obj, RakuAST::Signature, '$!parameters',
$parameters // self.IMPL-WRAP-LIST([]));
self.IMPL-UNWRAP-LIST($parameters)
) if $parameters;
nqp::bindattr($obj, RakuAST::Signature, '$!returns', $returns // RakuAST::Node);
nqp::bindattr_i($obj, RakuAST::Signature, '$!is-on-method', 0);
nqp::bindattr_i($obj, RakuAST::Signature, '$!is-on-named-method', 0);
Expand All @@ -28,6 +29,13 @@ class RakuAST::Signature
$obj
}

method parameters() {
self.IMPL-WRAP-LIST($!parameters // [])
}
method parameters-initialized() {
nqp::isconcrete($!parameters) ?? True !! False
}

method attach(RakuAST::Resolver $resolver) {
# If we're the signature for a method...
if $!is-on-method {
Expand Down Expand Up @@ -76,15 +84,17 @@ class RakuAST::Signature

method IMPL-HAS-PARAMETER(Str $name) {
return True if $name eq '%_' && $!implicit-slurpy-hash;
for self.IMPL-UNWRAP-LIST($!parameters) {
return True if $_.target && $_.target.lexical-name eq $name;
if $!parameters {
for $!parameters {
return True if $_.target && $_.target.lexical-name eq $name;
}
}
False
}

method IMPL-ENSURE-IMPLICITS() {
if $!is-on-method && !($!implicit-invocant || $!implicit-slurpy-hash) {
my @param-asts := self.IMPL-UNWRAP-LIST($!parameters);
my @param-asts := $!parameters // [];
unless @param-asts && @param-asts[0].invocant {
my $type;
if $!is-on-meta-method {
Expand Down Expand Up @@ -121,7 +131,7 @@ class RakuAST::Signature
}
}
if $!is-on-role-body && !$!implicit-invocant {
my @param-asts := self.IMPL-UNWRAP-LIST($!parameters);
my @param-asts := $!parameters // [];
unless @param-asts && @param-asts[0].invocant {
my $invocant := RakuAST::Parameter.new();
$invocant.add-type-capture(
Expand All @@ -142,8 +152,10 @@ class RakuAST::Signature
if $!implicit-invocant {
@parameters.push($!implicit-invocant.meta-object);
}
for self.IMPL-UNWRAP-LIST($!parameters) {
@parameters.push($_.meta-object);
if $!parameters {
for $!parameters {
@parameters.push($_.meta-object);
}
}
if $!implicit-slurpy-hash {
@parameters.push($!implicit-slurpy-hash.meta-object);
Expand Down Expand Up @@ -184,7 +196,7 @@ class RakuAST::Signature
method IMPL-QAST-BINDINGS(RakuAST::IMPL::QASTContext $context, :$needs-full-binder) {
self.IMPL-ENSURE-IMPLICITS();
my $bindings := QAST::Stmts.new();
my $parameters := self.IMPL-UNWRAP-LIST($!parameters);
my $parameters := $!parameters // [];
if $needs-full-binder {
$bindings.push(QAST::Op.new(
:op('if'),
Expand Down Expand Up @@ -215,39 +227,48 @@ class RakuAST::Signature
}

method set-default-type(RakuAST::Type $type) {
for self.IMPL-UNWRAP-LIST($!parameters) {
$_.set-default-type($type);
if $!parameters {
for $!parameters {
$_.set-default-type($type);
}
}
}

method arity() {
my int $arity := 0;
$arity++ if $!implicit-invocant;
for self.IMPL-UNWRAP-LIST($!parameters) {
last unless $_.is-positional && !$_.is-optional;
$arity++;
if $!parameters {
for $!parameters {
last unless $_.is-positional && !$_.is-optional;
$arity++;
}
}
nqp::box_i($arity, Int)
}

method count() {
my int $count := 0;
$count++ if $!implicit-invocant;
for self.IMPL-UNWRAP-LIST($!parameters) {
if $_.is-positional {
$count++;
}
elsif !($_.slurpy =:= RakuAST::Parameter::Slurpy) && $_.target.sigil ne '%' {
return nqp::box_n(nqp::inf, Num);
if $!parameters {
for $!parameters {
if $_.is-positional {
$count++;
}
elsif !($_.slurpy =:= RakuAST::Parameter::Slurpy)
&& $_.target.sigil ne '%' {
return nqp::box_n(nqp::inf, Num);
}
}
}
nqp::box_i($count, Int)
}

method visit-children(Code $visitor) {
$visitor($!implicit-invocant) if $!implicit-invocant;
for self.IMPL-UNWRAP-LIST($!parameters) {
$visitor($_);
if $!parameters {
for $!parameters {
$visitor($_);
}
}
$visitor($!implicit-slurpy-hash) if $!implicit-slurpy-hash;
$visitor($!returns) if $!returns;
Expand Down
25 changes: 25 additions & 0 deletions src/Raku/ast/variable-declaration.rakumod
Expand Up @@ -1371,6 +1371,7 @@ class RakuAST::VarDeclaration::Placeholder
is RakuAST::Attaching
is RakuAST::Term
is RakuAST::BeginTime
is RakuAST::CheckTime
{
has Bool $!already-declared;

Expand Down Expand Up @@ -1406,6 +1407,30 @@ class RakuAST::VarDeclaration::Placeholder
unless $!already-declared;
}

method PERFORM-CHECK(
RakuAST::Resolver $resolver,
RakuAST::IMPL::QASTContext $context
) {
my $signature := $resolver.find-attach-target('block').signature;
if $signature.parameters-initialized {
# @_ and %_ are only real placeholders if they were not
# already defined in the signature, so we need to check
# there before pulling the plug
my $name := self.lexical-name;
if $name eq '@_' || $name eq '%_' {
return True if $signature.IMPL-HAS-PARAMETER($name);
}
self.add-sorry(
$resolver.build-exception('X::Signature::Placeholder',
precursor => 1,
placeholder => self.lexical-name
)
);
return False;
}
True
}

method IMPL-ALREADY-DECLARED(Bool $declared) {
nqp::bindattr(self, RakuAST::VarDeclaration::Placeholder, '$!already-declared', $declared ?? True !! False);
}
Expand Down

0 comments on commit 6d9c4c7

Please sign in to comment.