# Macro type help

**URL:** <https://forum.crystal-lang.org/t/macro-type-help/912>\
**Category:** Help & Support\
**Created:** [June 29, 2019, 10:36pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912 "2019-06-29T22:36:50Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [June 29, 2019, 10:36pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/1 "2019-06-29T22:36:50Z")

</div>

`is_a` doesn’t work like I thought. Is there a macro version that will resolve the two errors below?

Each variable may be `Bytes?` or `StaticArray(UInt8)`.

```auto
    {% for ivar in @type.instance_vars %}
      {% if ann = ivar.annotation(Wipe::Var) %}
        if @{{ ivar.id }}.is_a?(StaticArray)
puts "wiping static {{ivar}}"
# Error: undefined method 'to_slice' for Nil (compile-time type is (Slice(UInt8) | Nil))
            Sodium.memzero @{{ ivar.id }}.to_slice
        else
          if var = @{{ ivar.id }}
# Error: no overload matches 'Sodium.memzero' with type StaticArray(UInt8,
            Sodium.memzero var
          end
        end
      {% end %}
    {% end %}

```

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [June 29, 2019, 10:46pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/2 "2019-06-29T22:46:56Z")

</div>

You could prob add that logic to the macro code. Something like

```auto
{% for ivar in @type.instance_vars %}
  {% if ann = ivar.annotation(Wipe::Var) %}
    {% if ivar.type.id == StaticArray.id %}
      puts "wiping static {{ivar}}"
      Sodium.memzero @{{ ivar.id }}.to_slice
    {% else %}
      if var = @{{ ivar.id }}
        Sodium.memzero var
      end
    {% end %}
  {% end %}
{% end %}

```

Doing type comparisons in macro land can be kinda a pain.

Another option is to use a `case`.

```auto
{% for ivar in @type.instance_vars %}
  {% if ann = ivar.annotation(Wipe::Var) %}
    val = @{{ivar.id}}
    return unless val
    case val
    when StaticArray then Sodium.memzero val.to_slice
    else
      Sodium.memzero var
    end
  {% end %}
{% end %}
```

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [June 29, 2019, 11:07pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/3 "2019-06-29T23:07:58Z")

</div>

You can’t restrict the type of an instance variable. Assign it first to a local variable.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [June 29, 2019, 11:27pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/4 "2019-06-29T23:27:04Z")

</div>

If I assign to a local variable StaticArray is copied defeating the purpose.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [June 29, 2019, 11:33pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/5 "2019-06-29T23:33:25Z")

</div>

```auto
class C
        @a = StaticArray(UInt8, 4).new 0

        def b
                b = @a.to_slice
                b[1] = 1_u8

                c = @a
                c.to_slice
                c[2] = 2_u8
        end
end

c = C.new

c.b
p c

```

The second modification doesn’t modify the original StaticArray.  
Output:

```auto
#<C:0x10c19eff0 @a=StaticArray[0, 1, 0, 0]>

```

---

<div class="post-metadata">

**Author:** ![bcardiff](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bcardiff/32/3_2.png) [@bcardiff](https://forum.crystal-lang.org/u/bcardiff)\
**Post date:** [June 29, 2019, 11:48pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/6 "2019-06-29T23:48:20Z")

</div>

You could assign it back to the ivar.  
Or maybe you can try luck with pointer to struct.

Or, if this is in the initializer, and you are 100% sure about the type, a call to `not_nil!` might be helpful to ensure the is well, not nil. A similar thing can be done with `as` to check the type. But you are opening the door to runtime cast errors.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [June 30, 2019, 2:57am UTC](https://forum.crystal-lang.org/t/macro-type-help/912/7 "2019-06-30T02:57:42Z")

</div>

@bcardiff The main problem is `if v.is_a?(StaticArray)`. In either case the compiler thinks it can be both types and never compiles. The comments in my first post show the errors.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [June 30, 2019, 3:00am UTC](https://forum.crystal-lang.org/t/macro-type-help/912/8 "2019-06-30T03:00:44Z")

</div>

My latest attempt combining a switch statement and assigning back to the ivar crashes. It’s not the memzero, but the assignment crashing it.

```auto
    {% for ivar in @type.instance_vars %}
      {% if ann = ivar.annotation(Wipe::Var) %}
        if var = @{{ ivar.id }}
          case var
          when StaticArray
puts "wiping {{ivar}}"
# Sodium.memzero var.to_slice
            @{{ ivar.id }} = var
          else
            Sodium.memzero var.to_slice
          end
        end
      {% end %}
    {% end %}

```

```auto
Invalid memory access (signal 11) at address 0x4
[0x105a6eb1b] *CallStack::print_backtrace:Int32 +107
[0x105a47fb5] __crystal_sigfault_handler +181
[0x7fff5f1d9b5d] _sigtramp +29
[0x105ae3184] *Sodium::Digest::Blake2b@Sodium::Wipe#wipe:(StaticArray(UInt8, 64) | Nil) +52
[0x105ae3097] *Sodium::Digest::Blake2b@Sodium::Wipe#finalize:(StaticArray(UInt8, 64) | Nil) +55
[0x105a499c7] ~procProc(Pointer(Void), Pointer(Void), (StaticArray(UInt8, 64) | Nil))@/usr/local/Cellar/crystal/0.28.0/src/gc/boehm.cr:130 +55
[0x105b208fe] GC_invoke_finalizers +146
[0x105b20a2e] GC_notify_or_invoke_finalizers +150
[0x105b1c55d] GC_try_to_collect_general +254
[0x105b1c591] GC_gcollect +13
[0x105a70729] *GC::collect:Nil +9
[0x105a4e571] *check_wiped<Slice(UInt8)>:Nil +49

```

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [June 30, 2019, 10:09am UTC](https://forum.crystal-lang.org/t/macro-type-help/912/9 "2019-06-30T10:09:40Z")

</div>

You could check the type directly in the macro code.

Also, it’s easier to help if you give us the full code. Otherwise we can only guess.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [June 30, 2019, 3:37pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/10 "2019-06-30T15:37:57Z")

</div>

[https://github.com/didactic-drunk/sodium.cr/blob/master/src/sodium/wipe.cr](https://github.com/didactic-drunk/sodium.cr/blob/master/src/sodium/wipe.cr)

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [June 30, 2019, 4:26pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/11 "2019-06-30T16:26:43Z")

</div>

Another alternative: use `is_a?` on an instance var, then use `.as(...)` inside the branch. It’s an extra type assertion but it will save you from an extra copy. However, note that `StaticArray` is always passed by copy so I’m not sure what you are doing there with `StaticArray` will work.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [June 30, 2019, 4:33pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/12 "2019-06-30T16:33:01Z")

</div>

This seems to work:

```crystal
  protected def wipe
    return if @closed

    {% for ivar in @type.instance_vars %}
      {% if ann = ivar.annotation(Wipe::Var) %}
        {% if ivar.type <= StaticArray %}
          Sodium.memzero @{{ivar.id}}.to_slice
        {% elsif ivar.type <= Slice %}
          Sodium.memzero @{{ivar.id}}
        {% end %}
      {% end %}
    {% end %}
  end

```

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [June 30, 2019, 8:45pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/13 "2019-06-30T20:45:04Z")

</div>

I tried:

```auto
{% if ivar.type == StaticArray %}

```

Instead of:

```auto
{% if ivar.type <= StaticArray %}

```

Why does \<= work?

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [June 30, 2019, 9:12pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/14 "2019-06-30T21:12:58Z")

</div>

The first is comparing the ivar’s type (with generics) to the type `StaticArray` (without generics).

```auto
StaticArray(Int32, 3) == StaticArray # => false
StaticArray(Int32, 3) <= StaticArray # => true

```

`<=` makes the comparison be like "if the ivar’s type includes or is a child of `StaticArray`" which it is.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [July 1, 2019, 4:08pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/15 "2019-07-01T16:08:19Z")

</div>

> [@asterite](#):
>
> ```auto
> {% for ivar in @type.instance_vars %}
> {% if ivar.type <= StaticArray %}
> 
> ```

Why doesn’t this work?

```auto
{% for ivar in @type.instance_vars %}
  {% if ivar.is_a? StaticArray %}

```

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [July 1, 2019, 4:21pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/16 "2019-07-01T16:21:00Z")

</div>

It’s because `is_a?` inside macro code checks against the AST node type. What you can put there is `Path`, `Generic`, `ClassDef`, etc. Maybe it’s not a bit intuitive, and it might be a good idea to be able to do `ivar.type.is_a?(...)`.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [July 1, 2019, 6:16pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/17 "2019-07-01T18:16:51Z")

</div>

Would it be more clear if `is_a?` worked in crystal and macros the same way with the ast node using `is_ast?` `is_an_ast?` `ast?` `ast_type?` ?

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [July 1, 2019, 6:48pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/18 "2019-07-01T18:48:18Z")

</div>

Well, it already works the same as in runtime code: variables and things in crystal macros are always ast nodes, and with `is_a?` you are actually asking “is it this type of node?”.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [July 1, 2019, 8:20pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/19 "2019-07-01T20:20:45Z")

</div>

```auto
class C
  @a = "a"
  @b = StaticArray(UInt8, 384).new 0

  def c
    {% for ivar in @type.instance_vars %}
      val = @{{ ivar.id }}
      p val.is_a?(String)
      {% if ivar.is_a? String %}
        puts "true"
      {% else %}
        puts "false"
      {% end %}
    {% end %}
  end
end

C.new.c

```

Output:

```auto
true
false
false
false

```

Expected Output:

```auto
true
false
true
false

```

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [July 1, 2019, 8:36pm UTC](https://forum.crystal-lang.org/t/macro-type-help/912/20 "2019-07-01T20:36:55Z")

</div>

I understand what you say, but imagine we eventually implement macros by compiling them into Crystal programs. The type of `ivar` will be some kind of `Var`, not a String. In fact, there are no `String`s in macro land, only `StringLiteral` instances. Only AST nodes are manipulated in macros, and AST nodes are the ones under this tree: [https://crystal-lang.org/api/0.29.0/Crystal/Macros.html](https://crystal-lang.org/api/0.29.0/Crystal/Macros.html)

[Next page](https://forum.crystal-lang.org/t/macro-type-help/912.md?page=2)
