テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(macro-of-inline report) ブロック内構造体定義のネストはOK

現在, マクロオブインラインについて分かってるバグの一つは, ブロックの中で構造体なりunionを定義されると, メンバの名前を書換えてしまうということだ. 他に, 無名構造体/unionを定義する場合もあり, こちらに対処するのは簡単だし自然なのだが, そのコードが明に構造体を定義した時とうまく整合せず苦しんでいる.

そこで, 最低限どこまで変形すればいいかを調べる.

一番の問題は, マクロ呼出しがネストしていった結果, 名前が衝突すると言った場合だろう. しかし, 以下のコードはコンパイル出来る. すなわち, このコードでは, 変数名としての2つのtをそれぞれランダムな名前に変更すればいい. これはつまり, 構造体の定義自体には手を入れなくていいということだ.

int main()
{
    struct T { int x; };
    do {
        struct T { int y; }; 
        struct T t;
        t.y = 2;
    } while (0);
    struct T t;
    t.x = 1;
    return 0;
}

ただし,

struct T { int x; } t;

のような宣言をされた場合は, tはリネーム必要だから, 構造体の宣言をされた場合ときれいに分離出来るアルゴリズムが必要となる (構造体を定義する時も, 変数を定義する時も同じくDeclノードである場合が問題をややこしくする. それ以下の形によって分離しなければならない)

例えば以下のような形も許される.

struct T { int x; } t = { .x = 0 };

地獄である.

もっとも安直なのは, Declノードの下にStructかUnionが来てる場合と区別することだが, こういう考え方は漏れに弱い. ちなみにこの悩みは, pycparserに手を入れる方が良い気がするので, 少し考えてみる.


結局, 色々とひねくれたパターンを追加した結果, 以下でやってみるべきという結論に達した. NamedInitializerをvisitした時にexprの方だけに潜るのは必然だと思う. Declのガードは, まずこれでやるのがもっともシンプルだと思う. これでダメだと分かった時に他を考えることにする.

diff --git a/macro_of_inline/rewrite_void_fun.py b/macro_of_inline/rewrite_void_fun.py
index 79fe567..6dcee06 100644
--- a/macro_of_inline/rewrite_void_fun.py
+++ b/macro_of_inline/rewrite_void_fun.py
@@ -66,6 +66,9 @@ class RenameVars(ext_pycparser.NodeVisitor):
                self.revertTable()

        def visit_Decl(self, node):
+               if isinstance(node.type, c_ast.Struct) or isinstance(node.type, c_ast.Union):
+                       return
+
                self.cur_table.register(node.name)
                alias = self.cur_table.alias(node.name)
                utils.P("Decl: %s -> %s" % (node.name, alias))
@@ -82,11 +85,9 @@ class RenameVars(ext_pycparser.NodeVisitor):
                """
                self.visit(node.name)

-       # FIXME
-       # If the block has declartion of struct or union which is not anonymous
-       # then the first visit is to Decl node. In there, it rewrites the members.
        def visit_Struct(self, n):
                pass
+
        def visit_Union(self, n):
                pass

@@ -99,6 +100,9 @@ class RenameVars(ext_pycparser.NodeVisitor):
                utils.P("ID: %s -> %s" % (node.name, alias))
                node.name = alias

+       def visit_NamedInitializer(self, n):
+               self.visit(n.expr)
+
        def switchTable(self):
                utils.P("switch table")
                self.cur_table.show()