Skip to content

Commit

Permalink
Make life of custom role creators easier
Browse files Browse the repository at this point in the history
- accept a `List` is role body return value
- tolerate no return value from the body
- otherwise try to throw something sensible
  • Loading branch information
vrurg committed Dec 17, 2023
1 parent 69d6168 commit 3c88689
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
36 changes: 32 additions & 4 deletions src/Perl6/Metamodel/ParametricRoleHOW.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,37 @@ class Perl6::Metamodel::ParametricRoleHOW
my $type_env;
my $error;
try {
my @result := $!body_block(|@pos_args, |%named_args);
$type_env := nqp::ifnull(Perl6::Metamodel::Configuration.type_env_from(@result[1]), @result[1]);
my $result := $!body_block(|@pos_args, |%named_args);
if nqp::isconcrete($result) {
# Support for bodies returning Raku's positional
my $original-result := $result;
if nqp::can($result, 'FLATTENABLE_LIST') {
$result := $result.FLATTENABLE_LIST();
}
if nqp::islist($result) && nqp::elems($result) == 2 {
$type_env :=
nqp::ifnull(Perl6::Metamodel::Configuration.type_env_from($result[1]), $result[1]);
}
else {
Perl6::Metamodel::Configuration.throw_or_die(
'X::Role::BodyReturn',
"Role '" ~ $obj.HOW.name($obj) ~ "' body block is expected to return a list, got '"
~ $original-result.HOW.name($original-result) ~ "' instead",
:role($obj),
:expected("a list of two elements"),
:got( (nqp::isconcrete($original-result)
?? "an object instance" !! "a type object")
~ " of type " ~ $original-result.HOW.name($original-result) ))
}
}
else {
# When there is no concrete return value from the body use empty TypeEnv then.
# Assuming that no Raku-generated role body would return an undefined value, especially those
# that belong to the core; and assuming that the only period of time when TypeEnv is not
# available on the configuration class is the early stages of the CORE.c compilation, – we can
# safely skip the check for nullness. Can't we?
$type_env := Perl6::Metamodel::Configuration.type_env_type().new;
}
CATCH {
$error := $!;
}
Expand All @@ -179,8 +208,7 @@ class Perl6::Metamodel::ParametricRoleHOW
"Could not instantiate role '" ~ self.name($obj)
~ "':\n" ~ ($exception || nqp::getmessage($error)),
:role($obj),
:exception($error)
)
:exception($error) )
}

# Use it to build a concrete role.
Expand Down
11 changes: 11 additions & 0 deletions src/core.c/Exception.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -1850,6 +1850,17 @@ my class X::Role::Instantiation is Exception does X::Wrapper {
}
}

my class X::Role::BodyReturn is Exception {
has Mu $.role is required;
has Str:D $.expected is required;
has Str:D $.got is required;
method message() {
"Bad return value of role '" ~ $!role.^name
~ "' body block; expected " ~ $.expected
~ " but got " ~ $.got
}
}

my class X::Role::Initialization is Exception {
method message() { "Can only supply an initialization value for a role if it has a single public attribute, but this is not the case for '{$.role.^name}'" }
}
Expand Down

0 comments on commit 3c88689

Please sign in to comment.