# New Ticket Created by   
# Please include the string:  [perl #127460]
# in the subject line of all future correspondence about this issue. 
# <URL: https://rt.perl.org/Ticket/Display.html?id=127460 >


Setting an int32 member of a CUnion (nested in a CStruct) to 0 from C 
causes the NativeCall layer to return a type object to the user.

This is a bit bizarre, but the files are below. I'm nesting a CUnion 
within a CStruct in order to match the layout of the C struct.

-- bug.pm6 --
use NativeCall;

constant ZERO = 0;
constant TYPE_BOOL = 2;

class Inline::Scheme::Guile::AltType is repr('CUnion') {
         has int32 $.int_content; # When this is populated with 0, 
NativeCall returns the I::S::G::AltType type rather than an instance of 
the object.
         has Str   $.string_content;
}

class Inline::Scheme::Guile::ConsCell is repr('CStruct') {
         has int32 $.type;
         HAS Inline::Scheme::Guile::AltType $.content; # The nested 
CUnion is here.
}

class Inline::Scheme::Guile {
         sub native(Sub $sub) {
                 my Str $path = %?RESOURCES<libraries/guile-helper>.Str;
                 die "unable to find libguile-helper library"
                         unless $path;
                 trait_mod:<is>($sub, :native($path));
         }

         sub run( Str $expression,
                  &marshal_guile 
(Pointer[Inline::Scheme::Guile::ConsCell]) )
                    { ... }
                 native(&run);

         method run( Str $expression ) {
                 my @stuff;
                 my $ref = sub ( Pointer[Inline::Scheme::Guile::ConsCell] 
$cell )                        {
                         CATCH {
                                 warn "Don't die in callback, warn 
instead.\n";
                                 warn $_;
                         }
                         my $type = $cell.deref.type;
                         given $type {
                                 when TYPE_BOOL {
                                         my $content = 
$cell.deref.content; # Content comes back as the AltType type object 
rather than an instance.
                                         if $content.int_content == 1 {
                                                 @stuff.push( True );
                                         }
                                         else {
                                                 @stuff.push( False );
                                         }
                                 }
                         }
                 }
                 run( $expression, $ref );
                 return @stuff;
         }
}
--cut here--

-- bug.c --
#include <libguile.h>
#include <stdio.h>

typedef enum {
         VOID = -1,
         ZERO = 0,
         TYPE_BOOL = 2,
}
cons_cell_type;

typedef struct {
         cons_cell_type type;
         union {
                 long  int_content; // This is the problem.
                 char* string_content;
         };
}
cons_cell;

static void _walk_scm( SCM scm, cons_cell* result ) {
         int num_values = scm_c_nvalues( scm );

         // '#f' is not null, bool, false and only 1 value.
         //
         if (    num_values == 1 &&
                 //scm_is_null( scm ) &&
                 scm_is_bool( scm ) &&
                 scm_is_false( scm ) ) {
                 result[0].type = TYPE_BOOL;
//              result[0].int_content = -1; // Assigning -1 to 
int_content returns an instance
                 result[0].int_content = 0; // Assigning 0 to int_content 
returns the type object.
                 result[1].type = ZERO;
                 return;
         }

         // '#t' is not null, bool, not false, true and only 1 value.
         //
         if (    num_values == 1 &&
                 //scm_is_null( scm ) &&
                 scm_is_bool( scm ) &&
                 !scm_is_false( scm ) &&
                 scm_is_true( scm ) ) {
                 result[0].type = TYPE_BOOL;
                 result[0].int_content = 1;
                 result[1].type = ZERO;
                 return;
         }
}

void* _run( void* expression ) {
         SCM str = scm_from_latin1_string( (char*) expression );
         SCM scm = scm_eval_string( str );

         // Sigh, special-case void lists.
         if ( scm_c_nvalues( scm ) == 0 ) {
                 cons_cell* result = malloc( sizeof( cons_cell ) * 2 );
                 result[0].type = VOID;
                 result[1].type = ZERO;
                 return result;
         }

         cons_cell* result = malloc( sizeof( cons_cell ) * 2 );

         _walk_scm( scm, result );

         return result;
}

void run( const char* expression, void (*unmarshal(void*)) ) {
         cons_cell* cells = scm_with_guile( _run, (void*)expression );
         cons_cell* head = cells;

         while( head->type != ZERO ) {
                 unmarshal(head++);
         }

         free(cells);
}
--cut here--



-- t/00-core.t --
#!/usr/bin/env perl6

use v6;
use Test;

use NativeCall;

plan 3;

use Inline::Scheme::Guile;

my $g = Inline::Scheme::Guile.new;

is-deeply [ $g.run( q{#f} ) ],   [ False ], q{value (#f)}; # This should 
segfault when 0 is used as False rather than -1.
is-deeply [ $g.run( q{#t} ) ],   [ True  ], q{value (#t)}; # This should 
work regardless.
--cut here--

Reply via email to