Skip to content

Commit

Permalink
[dotnet] Instantiate protos in inner routines to create dispatch rout…
Browse files Browse the repository at this point in the history
…ines with the candidate list for that block. We actually do this by copying the most immediately outer instantiation's candidate list and then adding the new ones to the list, which seems likely-ish to get closure semantics right while giving the semantics as per spec. It's very much an instantiation in that it shares body and other bits with the original proto, though.
  • Loading branch information
jnthn committed Oct 30, 2010
1 parent 0d78282 commit aa4bd85
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 11 deletions.
48 changes: 37 additions & 11 deletions dotnet/compiler/Actions.pm
Expand Up @@ -503,29 +503,55 @@ method routine_def($/) {
# Otherwise, no candidate holder, so add one.
else {
# Check we have a proto in scope.
# XXX We need to get the proto clone from outer in place
# but for now just handle it being in the same block.
unless %sym<proto> {
$/.CURSOR.panic("Sorry, can only declare multis in the same block as a proto so far");
if %sym<proto> {
# WTF, a proto is in this scope, but didn't set up a
# candidate holder?!
$/.CURSOR.panic('Internal Error: Current scope has a proto, but no candidate list holder was set up. (This should never happen.)');
}
my $found_proto;
for @BLOCK {
my %sym := $_.symbol($name);
if %sym<proto> || %sym<cholder> {
$found_proto := 1;
}
elsif %sym {
$/.CURSOR.panic("Cannot declare a multi when an only is already in scope.");
}
}

# If we didn't find a proto, error for now.
unless $found_proto {
$/.CURSOR.panic("Sorry, no proto sub in scope, and auto-generation of protos is not yet implemented.");
}

# Valid to add a candidate holder, so do so.
# Set up dispatch routine in this scope.
$cholder := PAST::Op.new( :pasttype('list') );
@BLOCK[0][0].push(PAST::Op.new(
:pasttype('nqpop'), :name('set_dispatchees'),
PAST::Var.new( :name($name) ),
my $dispatch_setup := PAST::Op.new(
:pasttype('nqpop'), :name('create_dispatch_and_add_candidates'),
PAST::Var.new( :name($name), :scope('outer') ),
$cholder
));
@BLOCK[0].symbol($name, :cholder($cholder));
);
@BLOCK[0][0].push(PAST::Var.new( :name($name), :isdecl(1),
:viviself($dispatch_setup), :scope('lexical') ) );
@BLOCK[0].symbol($name, :scope('lexical'), :cholder($cholder) );
}

# Add this candidate to the holder.
$cholder.push($past);
}
elsif $*MULTINESS eq 'proto' {
# Create a candidate list holder for the dispatchees
# this proto will work over, and install them along
# with the proto.
my $cholder := PAST::Op.new( :pasttype('list') );
@BLOCK[0][0].push(PAST::Var.new( :name($name), :isdecl(1),
:viviself($past), :scope('lexical') ) );
@BLOCK[0].symbol($name, :scope('lexical'), :proto(1) );
@BLOCK[0][0].push(PAST::Op.new(
:pasttype('nqpop'), :name('set_dispatchees'),
PAST::Var.new( :name($name) ),
$cholder
));
@BLOCK[0].symbol($name, :scope('lexical'), :proto(1), :cholder($cholder) );
}
else {
@BLOCK[0][0].push(PAST::Var.new( :name($name), :isdecl(1),
Expand Down
44 changes: 44 additions & 0 deletions dotnet/runtime/Runtime/Ops.cs
Expand Up @@ -580,6 +580,50 @@ public static RakudoObject set_dispatchees(ThreadContext TC, RakudoObject CodeOb
}
}

/// <summary>
/// Creates an instantiation of the dispatch routine (or proto, which may
/// serve as one) supplied and augments it with the provided candidates.
/// It relies on being passed the instantiation of the dispatcher from the
/// last outer scope that had an instantiation, and we thus take its
/// candidates. This may or may not hold up in the long run; it works out
/// in the Perl 6-y "you can make a new instance from any object" sense
/// though, and seems more likely to get the closure semantics right than
/// any of the other approaches I've considered so far.
/// </summary>
/// <param name="TC"></param>
/// <param name="ToInstantiate"></param>
/// <param name="ExtraDispatchees"></param>
/// <returns></returns>
public static RakudoObject create_dispatch_and_add_candidates(ThreadContext TC, RakudoObject ToInstantiate, RakudoObject ExtraDispatchees)
{
// Make sure we got the right things.
var Source = ToInstantiate as RakudoCodeRef.Instance;
var AdditionalDispatchList = ExtraDispatchees as P6list.Instance;
if (Source == null || AdditionalDispatchList == null)
throw new Exception("create_dispatch_and_add_candidates expects a RakudoCodeRef and a P6list");

// Clone all but SC (since it's a new object and doesn't live in any
// SC yet) and dispatchees (which we want to munge).
var NewDispatch = new RakudoCodeRef.Instance(Source.STable);
NewDispatch.Body = Source.Body;
NewDispatch.CurrentContext = Source.CurrentContext;
NewDispatch.Handlers = Source.Handlers;
NewDispatch.OuterBlock = Source.OuterBlock;
NewDispatch.OuterForNextInvocation = Source.OuterForNextInvocation;
NewDispatch.Sig = Source.Sig;
NewDispatch.StaticLexPad = Source.StaticLexPad;

// Take existing candidates and add new ones.
NewDispatch.Dispatchees = new RakudoObject[Source.Dispatchees.Length + AdditionalDispatchList.Storage.Count];
var i = 0;
for (int j = 0; j < Source.Dispatchees.Length; j++)
NewDispatch.Dispatchees[i++] = Source.Dispatchees[j];
for (int j = 0; j < AdditionalDispatchList.Storage.Count; j++)
NewDispatch.Dispatchees[i++] = AdditionalDispatchList.Storage[j];

return NewDispatch;
}

/// <summary>
/// Gets a value at a given positional index from a low level list
/// (something that uses the P6list representation).
Expand Down

0 comments on commit aa4bd85

Please sign in to comment.