Oracle® Developer Studio 12.5: C ユーザーズガイド

印刷ビューの終了

更新: 2016 年 7 月
 
 

2.12 プラグマ

次の形式の前処理行は、実装定義アクションを指定します。

#pragma pp-tokens

次の #pragmas はコンパイルシステムに認識されます。認識されなかったプラグマは無視されます。–v オプションを使用すると、認識されないプラグマに関する警告が生成されます。

2.12.1 align

#pragma align integer (variable[, variable])

整列プラグマで指定した変数のメモリーはデフォルト値によらず、すべて integer バイト境界にそろえられます。ただし、次の制限があります。

  • integer の値は、1 から 128 までの 2 の累乗でなければいけません。有効な値は、1、2、4、8、16、32、64、および 128 です。

  • variable は大域または静的変数です。

  • 指定された境界がデフォルトより小さい場合は、デフォルトが優先します。

  • プラグマ行は、それが示す変数の宣言よりも先になければいけません。それ以外の場合は無視されます。

  • プラグマ行で記述されているが、そのあとで宣言されていない変数は無視されます。例:

    #pragma align 64 (aninteger, astring, astruct)
    int aninteger;
    static char astring[256];
    struct astruct{int a; char *b;};

2.12.2 c99

#pragma c99(“implicit” | “no%implicit”)

このプラグマは、暗黙的な関数宣言の診断を制御します。c99 プラグマの値が "implicit" に設定されている場合 (引用符を使用)、コンパイラが暗黙的な関数宣言を検出すると、警告が生成されます。c99 プラグマの値が "no%implicit" に設定されている場合 (引用符を使用)、プラグマの値がリセットされるまで、コンパイラは暗黙的な関数宣言をサイレントに受け入れます。

-std オプションの値は、このプラグマのデフォルトの状態に影響を与えます。-std=c11 または -std=c99 の場合、デフォルト状態は #pragma c99(“implicit”) です。-std=c89 の場合、デフォルト状態は #pragma c99(“no%implicit”) です。

2.12.3 does_not_read_global_data

#pragma does_not_read_global_data (funcname [, funcname])

リストに指定したルーチンが直接にも間接にも大域データを読み取らないことを表明します。この動作により、そうしたルーチンへの呼び出しの前後にあるコードがより適切に最適化されます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。

指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。

2.12.4 does_not_return

#pragma does_not_return (funcname [, funcname])

指定した関数への呼び出しが復帰しないことをコンパイラに表明します。この場合、コンパイラは、その仮定に一貫性を持つ最適化を実行できます。たとえば、レジスタの存続期間が呼び出し元で終了する場合は、さらに最適化率を高めることができます。

指定した関数が復帰した場合は、プログラムの動作は未定義になります。次の例に示すように、このプラグマは、指定した関数をプロトタイプまたは空のパラメータリストで宣言したあとでのみ許可されます。

extern void exit(int);
#pragma does_not_return(exit)

extern void __assert(int);
#pragma does_not_return(__assert)

2.12.5 does_not_write_global_data

#pragma does_not_write_global_data (funcname [, funcname])

リストに指定したルーチンが直接にも間接にも大域データを書き込まないことを表明します。この動作により、そうしたルーチンへの呼び出しの前後にあるコードがより適切に最適化されます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。

指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。

2.12.6 dumpmacros

#pragma dumpmacros(value[,value...])

マクロがプログラム内でどのように動作しているかを調べたいときに、このプラグマを使用します。このプラグマは、定義済みマクロ、解除済みマクロ、実際の使用状況といった情報を提供します。マクロの処理順序に従って、標準エラー (stderr) に出力します。dumpmacros プラグマは、ファイルが終わるまで、または #pragma end_dumpmacros に到達するまで、有効です。end_dumpmacrosを参照してください。次の表に、value の可能な値を示します。

意味
defs
すべての定義済みマクロを出力します
undefs
すべての解除済みマクロを出力します
use
使用されているマクロの情報を出力します
loc
defsundefsuse の位置 (パス名と行番号) も出力します
conds
条件付き指令で使用したマクロの使用情報を出力します
sys
システムヘッダーファイルのマクロについて、すべての定義済みマクロ、解除済みマクロ、使用状況も出力します

注 -  サブオプション loccondssys は、オプション defsundefsuse の修飾子です。locconds、および sys は、単独では効果はありません。たとえば #pragma dumpmacros(loc,conds,sys) には、何も効果はありません。

dumpmacros プラグマとコマンド行オプションの効果は同じですが、プラグマはコマンド行オプションをオーバーライドします。-xdumpmacros[=value[,value...]]を参照してください。

dumpmacros プラグマは入れ子にならないので、次のコードでは #pragma end_dumpmacros が処理されるとマクロ情報の出力が停止します。

#pragma dumpmacros(defs, undefs)
#pragma dumpmacros(defs, undefs)
...
#pragma end_dumpmacros

dumpmacros プラグマの効果は累積的です。次のものは、

#pragma dumpmacros(defs, undefs)
#pragma dumpmacros(loc)

次と同じ効果を持ちます。

#pragma dumpmacros(defs, undefs, loc)

オプション #pragma dumpmacros(use,no%loc) を使用した場合、使用したマクロそれぞれの名前が一度だけ出力されます。オプション #pragma dumpmacros(use,loc) を使用した場合、マクロを使用するたびに位置とマクロ名が出力されます。

2.12.7 end_dumpmacros

#pragma end_dumpmacros

このプラグマは、dumpmacrospragma が終わったことを通知し、マクロ情報の出力を停止します。dumpmacros プラグマ終了時に end_dumpmacros プラグマを使用しなかった場合、dumpmacros プラグマはファイルが終わるまで出力を生成し続けます。

2.12.8 error_messages

#pragma error_messages (on|off|default, tag… tag)

error_messages プラグマは、ソースプログラム内で、C コンパイラおよび lint が発行するメッセージの制御を提供します。C コンパイラでは、警告メッセージに対してのみ有効です。

  • #pragma error_messages (on, tag… tag)

    on オプションは、先行するすべての #pragma error_messages オプション (off オプションなど) のスコープを終了し、–erroff オプションの効果をオーバーライドします。

  • #pragma error_messages (off, tag… tag)

    off オプションは、C コンパイラまたは lint プログラムが指定トークンから始まる特定のメッセージを発行することを禁止します。指定したエラーメッセージに対するプラグマのスコープは、別の error_messages プラグマによって無効にされるか、コンパイルが終了するまで有効なままです。

  • #pragma error_messages (default, tag… tag)

    default オプションは、指定タグについて、先行する #pragma error_messages ディレクティブのスコープを終了します。

  • #pragma error_messages will not suppress messages issued by lint pass 2.

    lint パスの詳細は、lint 使用方法を参照してください。

2.12.9 fini

#pragma fini (f1[, f2…,fn]

main() ルーチンを呼び出したあと、f1 から fn (終了関数) までの関数を呼び出します。そのような関数では、型が void で、かつ引数を一切受け入れないことが期待されます。これらが呼び出されるのは、プログラムがプログラム制御下で終了したとき、または包含元の共有オブジェクトがメモリーから削除されたときです。初期化関数の場合と同様、終了関数もリンクエディタによって処理された順に実行されます。

大域プログラム状態が初期化関数の影響を受ける場合には注意が必要です。たとえばシステムライブラリ終了関数を使用したときに何が発生するかがインタフェースに明示的に規定されていないかぎり、システムライブラリ初期化関数によって変更される可能性がある errno の値などの大域状態情報をすべて取得し、復元するようにしてください

このような関数は #pragma fini 指令の中に登場するたびに、1 回呼び出されます。

2.12.10 hdrstop

#pragma hdrstop

同じプリコンパイル済みヘッダーファイルを共有すべき各ソースファイルの活性文字列 (viable prefix) の最後を識別するために、hdrstop プラグマを最後のヘッダーファイルのあとに置く必要があります。たとえば次のファイルがあるとします。

example% cat a.c
#include "a.h"
#include "b.h"
#include "c.h"
#include <stdio.h>
#include "d.h"
.
.
.
example% cat b.h
#include "a.h"
#include "b.h"
#include "c.h"

活性文字列は c.h で終わるので、各ファイルの c.h のあとに #pragma hdrstop を挿入します。

#pragma hdrstop は、cc コマンドで指定されるソースファイルの活性文字列の最後にのみ出現する必要があります。#pragma hdrstopinclude ファイル内に指定しないでください。

2.12.11 ident

#pragma ident string

実行可能プログラムの .comment セクション内に任意の string を格納します。

2.12.12 init

#pragma init (f1[, f2…,fn])

main() を呼び出す前に、f 1 から f n までの関数 (初期化関数) を呼び出します。そのような関数では、型が void で、かつ引数を一切受け入れないことが期待されます。これらは、プログラムの実行開始時にそのメモリーイメージを構築している間に呼び出されます。共有オブジェクトの初期設定子は、共有オブジェクトをメモリーに入れる操作中、つまりプログラムの起動時または dlopen() などの一部の動的ロード時のいずれかに実行されます。初期化関数の呼び出しを順序付ける方法は、それがリンクエディタによって動的または静的に処理される順序に依存します。

初期化関数が大域プログラム状態に影響を与えるときは特に注意してください。たとえばシステムライブラリを終了関数として使用したときに何が発生するかがインタフェースに明示的に規定されていないかぎり、システムライブラリ初期化関数によって変更される可能性がある errno の値などの大域状態情報をすべて取得し、復元するようにしてください

このような関数は #pragma init 指令の中に登場するたびに、1 回呼び出されます。

2.12.13 inline

#pragma [no_]inline (funcname[, funcname])

指定したルーチン名のインライン化を制御します。このプラグマはファイル全体に対して有効です。このプラグマでは、大域インライン化制御のみが許可され、呼び出し元固有の制御は許可されません。

#pragma inline は、現在のファイル内の呼び出しのうち、プラグマ内でリストされているルーチンのリストに一致するものをインライン化するヒントを、コンパイラに提供します。このヒントは、状況によっては無視されることがあります。たとえば、関数本体が別のモジュールに存在していて、crossfile オプションが使用されていない場合などです。

#pragma no_inline は、現在のファイル内の呼び出しのうち、プラグマにリストされたルーチンのリストに一致するものをインライン化しないというヒントを、コンパイラに提供します。

次の例に示すように、#pragma inline および #pragma no_inline は、関数がプロトタイプまたは空のパラメータリストで宣言されたあとでのみ許可されます。

static void foo(int);
static int bar(int, char *);
#pragma inline(foo, bar)

詳細は、コンパイラオプション -xldscope-xinline-xO、および -xipo の説明を参照してください。

2.12.14 int_to_unsigned

#pragma int_to_unsigned (funcname)

–Xt または –Xs モードで unsigned の型を返す関数について、その関数の戻り値が型 int になるように変更します。

2.12.15 must_have_frame

#pragma must_have_frame(funcname[,funcname])

このプラグマは、(System V ABI で定義されているとおり) 完全なスタックフレームを必ず持つように、指定した関数リストをコンパイルすることを要求します。このプラグマで関数を列挙する前に、関数のプロトタイプを宣言する必要があります。

extern void foo(int);
extern void bar(int);
#pragma must_have_frame(foo, bar)

このプラグマを使用できるのは、指定した関数のプロトタイプの宣言後のみに限定されます。プラグマは関数の最後より先に記述する必要があります。

void foo(int) {
  .
  #pragma must_have_frame(foo)
  .
  return;
  }

2.12.16 nomemorydepend

#pragma nomemorydepend

(SPARC) このプラグマは、ループのどの繰り返し内でも、同じメモリーアドレスへの参照によって引き起こされるメモリーの依存関係が発生しないように指定します。このプラグマは、コンパイラがループの 1 回の繰り返しの中で、より効率的に命令をスケジュールすることを許可します。ループの繰り返しの中でメモリーの依存があると、プログラムの実行結果は未定義になります。コンパイラはこの情報をレベル 3 以上の最適化に利用します。

このプラグマのスコープは、プラグマから始まり、次のブロックの先頭、現在のブロック内の次の for ループ、現在のブロックの末尾のいずれか最初に発生した状況で終わります。プラグマは、スコープの終端に到達した時点で最初に見つかった for ループに適用されます。

2.12.17 no_side_effect

#pragma no_side_effect(funcname[, funcname…])

funcname には、現行の翻訳単位内の関数名を指定します。関数は、プラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。またプラグマはその関数の定義より前に指定されていなければいけません。指定された関数 funcname について、このプラグマは、その関数がどのような種類の副作用も一切持たず、渡された引数のみに依存する結果値を返すことを宣言します。さらに、funcname とそこから呼び出されたすべての子孫関数は、次のように振る舞います。

  • 呼び出し時点で呼び出し側が認識できるプログラム状態の一部に、読み出しまたは書き込みのためにアクセスすることはありません。

  • 入出力を実行しません。

  • 呼び出し時点で認識できるプログラム状態のどの部分も変更しません。

コンパイラはこの情報を、その関数を用いる最適化に利用することができます。関数に副作用があると、この関数を呼び出すプログラムの実行結果は未定義になります。コンパイラはこの情報をレベル 3 以上の最適化に利用します。

2.12.18 opt

#pragma opt level (funcname[, funcname])

funcname には、現行の翻訳単位内で定義された関数名を指定します。level の値は、指定した関数に対する最適化レベルです。0、1、2、3、4、または 5 の最適化レベルを割り当てることができます。level を 0 に設定することで、最適化を無効にできます。関数は、プラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。プラグマは、最適化する関数の定義の前に存在する必要があります。

このプラグマにリストされているすべての関数の最適化のレベルが -xmaxopt の値に下げられます。–xmaxopt=off の場合、このプラグマは無視されます。

2.12.19 pack

#pragma pack(n)

#pragma pack(n) を使用すると、構造体または共用体のメンバーの pack に影響を及ぼします。構造体または共用体のメンバーはデフォルトでは、char 型の場合は 1 バイト、short 型の場合は 2 バイト、整数型の場合は 4 バイト、といった具合に、その自然境界で整列されます。n が存在する場合、それは、任意の構造体または共用体メンバーに対するもっとも厳格な自然整列を指定する 2 の累乗でなければいけません。ゼロは受け入れられません。

#pragma pack(n) 指令は、次の pack 指令までのすべての構造体または共用体の定義に適用されます。別の翻訳単位で同じ構造体または共用体に対して異なる #pragma pack の定義が行われている場合、プログラムは予期しない形でコンパイルに失敗することがあります。特に、#pragma pack(n) は、事前にコンパイルされたライブラリのインタフェースを定義するヘッダーをインクルードする前には使用しないでください。#pragma pack(n) は、プログラムコード内の境界整列を変更するすべての構造体または共用体の直前に挿入することをお勧めします。そして、その構造体の直後に #pragma pack( ) を続けてください。

#pragma pack(n) を使用すると、構造体または共用体のメンバーの境界整列を指定できます。たとえば、#pragma pack(2) を指定すると、int、long、long long、float、double、long double およびポインタが、それぞれの自然整列境界ではなく、2 バイト境界に整列されます。

n がプラットフォームでもっとも厳密な整列を指示する値 (-m32 の x86 では 4、—m32 の SPARC では 8、-m64 の SPARC では 32) か、それより大きな値の場合は、自然境界整列が有効になります。n が省略された場合も、メンバーは自然境界整列に戻ります。

#pragma pack を使用する場合、構造体または共用体自身の整列条件は、その構造体または共用体でより厳密に境界整列されるメンバーの整列条件と同一です。したがって、その struct または union の任意の宣言は、pack の境界整列となります。たとえば、char 型だけの struct は整列の制限はありませんが、double 型を含む struct は 8 バイトの境界上に並びます。


注 -  #pragma pack を使用して構造体または共用体のメンバーを自然境界以外の境界で整列させると、通常、これらのフィールドへのアクセスが発生した場合に SPARC 上でバスエラーが起きます。このエラーを回避するには、必ず -xmemalign オプションも指定してください。このようなプログラムをコンパイルする最適な方法については、-xmemalign=ab を参照してください。
#pragma pack(push[,n])

現在の整列設定を内部スタックにプッシュし、オプションで n の指定された値で新しい整列を設定するには、#pragma pack(push[,n]) を使用します。

#pragma pack(pop)

整列を以前のプッシュで内部スタックに保存された最後の値に設定し、そのスタックエントリを削除 (またはポップ) するには、#pragma pack(pop) を使用します。


注 -  #pragma pack(n) は、内部スタックに影響を与えません。

2.12.20 pipeloop

#pragma pipeloop(n)

このプラグマは、引数 n に正の整定数または 0 を受け入れます。このプラグマは、ループがパイプライン化可能で、ループによる依存の最小の依存距離が n であることを指定します。距離が 0 の場合、そのループは実質的には Fortran 形式の doall ループで、ターゲットプロセッサ上でパイプライン処理するべきであることを意味します。距離が 0 より大きい場合、コンパイラは n 回だけの連続繰り返しでパイプラインを試みます。コンパイラはこの情報をレベル 3 以上の最適化に利用します。

このプラグマのスコープは、プラグマから始まり、次のブロックの先頭、現在のブロック内の次の for ループ、現在のブロックの末尾の中で最初に発生した状況で終わります。プラグマは、スコープの終端に到達した時点で最初に見つかった for ループに適用されます。

2.12.21 rarely_called

#pragma rarely_called(funcname[, funcname])

指定した関数があまり使用されないというヒントをコンパイラに与えます。この場合、コンパイラは、プロファイル収集段階のオーバーヘッドなしで、ルーチンの呼び出し元でプロファイルフィードバック方式の最適化を行うことができます。このプラグマはヒントなので、コンパイラは、このプラグマに基づく最適化を行わないことを選択できます。

指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。次の例は、#pragma rarely_called を示したものです。

extern void error (char *message);
#pragma rarely_called(error)

2.12.22 redefine_extname

#pragma redefine_extname old_extname new_extname

このプラグマにより、オブジェクトコード中で外部定義された old_extname の名前が new_extname に置換されます。この結果、リンク時にのみリンカーは名前 new_extname を認識します。関数定義、初期設定子、または式のいずれかとして old_extname を最初に使用したあと、#pragma redefine_extname が指定されていると、結果は未定義になります。-Xs のモードではこのプラグマはサポートされていません。

#pragma redefine_extname を使用できる場合、コンパイラは、事前に定義されたマクロの __PRAGMA_REDEFINE_EXTNAME の定義を提供するため、これを使用して、#pragma redefine_extname の有無に関係なく機能する移植可能なコードを作成できます。

#pragma redefine_extname は、関数名を変更できないときに関数インタフェースを効率的に再定義する手段を提供します。たとえば、既存プログラムとの互換性のために元の関数定義と、新しいプログラムで使用するために同じ関数の新しい定義をライブラリ内で保持する必要がある場合は、その新しい関数定義を新しい名前でライブラリに追加できます。その後、その関数を宣言するヘッダーファイルが #pragma redefine_extname を使用するので、その関数のすべての使用は、その関数の新しい定義にリンクされます。

#if    defined(__STDC__)

#ifdef __PRAGMA_REDEFINE_EXTNAME
extern int myroutine(const long *, int *);
#pragma redefine_extname myroutine __fixed_myroutine
#else /* __PRAGMA_REDEFINE_EXTNAME */

static int
myroutine(const long * arg1, int * arg2)
{
    extern int __myroutine(const long *, int*);
    return (__myroutine(arg1, arg2));
}
#endif /* __PRAGMA_REDEFINE_EXTNAME */

#else /* __STDC__ */

#ifdef __PRAGMA_REDEFINE_EXTNAME
extern int myroutine();
#pragma redefine_extname myroutine __fixed_myroutine
#else /* __PRAGMA_REDEFINE_EXTNAME */

static int
myroutine(arg1, arg2)
    long *arg1;
    int *arg2;
{
    extern int __fixed_myroutine();
    return (__fixed_myroutine(arg1, arg2));
}
#endif /* __PRAGMA_REDEFINE_EXTNAME */

#endif /* __STDC__ */

2.12.23 returns_new_memory

#pragma returns_new_memory (funcname[, funcname])

指定した関数の戻り値が呼び出し元のどのメモリーとも別名処理されないことを表明します。つまり、この呼び出しでは、新しいメモリー位置が返されます。この情報は、オプティマイザがポインタ値の追跡やメモリー位置の明確化をより適切に行うことを可能にするため、スケジューリング、パイプライン化、およびループの並列化が改善されます。表明が偽の場合には、プログラムの動作は未定義になります。

次の例に示すように、このプラグマは、指定した関数をプロトタイプまたは空のパラメータリストで宣言したあとでのみ許可されます。

void *malloc(unsigned);
#pragma returns_new_memory(malloc)

2.12.24 unknown_control_flow

#pragma unknown_control_flow (funcname[, funcname])

呼び出し元のフローグラフを変更する手続きを記述するには、#pragma unknown_control_flow 指令を使用します。通常、この指令には setjmp() のような関数の宣言が伴います。Oracle Solaris のシステム上では、インクルードファイル <setjmp.h> に次のコードが含まれています。

extern int setjmp();
#pragma unknown_control_flow(setjmp)

setjmp() のような特性を持つほかの関数も、同様に宣言する必要があります。

原則として、この属性を認識するオプティマイザは、制御フローグラフに適切な境界を挿入できます。これによって、setjmp() を呼び出す関数内で関数呼び出しを安全に処理しながら、影響を受けないフローグラフ部分のコードを最適化する機能を保持します。

指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。

2.12.25 unroll

#pragma unroll (unroll_factor)

このプラグマは、引数 unroll_factor に正の整定数を受け入れます。unroll_factor を 1 以外に設定することは、指定されたループを指定の係数で展開すべきである (可能なとき) という、コンパイラに対するヒントとして機能します。unroll_factor が 1 の場合、この指令は、コンパイラにループを展開しないよう指示します。コンパイラはこの情報をレベル 3 以上の最適化に利用します。

このプラグマのスコープは、プラグマから始まり、次のブロックの先頭、現在のブロック内の次の for ループ、現在のブロックの末尾のいずれか最初に発生した状況で終わります。プラグマは、スコープの終端に到達した時点で最初に見つかった for ループに適用されます。

2.12.26 warn_missing_parameter_info

#pragma [no_]warn_missing_parameter_info

#pragma warn_missing_parameter_info を指定すると、パラメータ型情報が含まれない関数宣言を持つ関数の呼び出しに対して警告が発生します。次の例を考えてみましょう。

example% cat -n t.c
     1    #pragma warn_missing_parameter_info
     2    
     3    int foo();
     4    
     5    int bar () {
     6    
     7       int i;
     8    
     9       i = foo(i);
    10    
    11       return i;
    12    }
% cc t.c -c -errtags
"t.c", line 9: warning: function foo has no prototype (E_NO_MISSED_PARAMS_ALLOWED)
example%

#pragma no_warn_missing_parameter_info は、それ以前の #pragma warn_missing_parameter_info を無効にします。

デフォルトでは、#pragma no_warn_missing_parameter_infoは有効です。

2.12.27 weak

#pragma weak symbol1 [= symbol2]

このプラグマは「弱い大域シンボル」を定義します。これは主に、ライブラリの構築時に使用されます。リンカーは弱いシンボルを解決できなくてもエラーメッセージを表示しません。

#pragma weak symbol

これは symbol を弱いシンボルとして定義しています。symbol の定義が見つからなくても、リンカーはエラーメッセージ等を出さなくなります。

#pragma weak symbol1 = symbol2

これは symbol1 を、symbol2 の別名である弱いシンボルと定義します。この形式のプラグマは、ソースファイルまたはそこにインクルードされたヘッダーファイルのいずれかで、symbol2 を定義した同じ変換ユニットの中にかぎり使用できます。それ以外で使用された場合は、コンパイルエラーになります。

プログラムが symbol1 を呼び出すけれども定義しておらず、symbol1 がリンクするライブラリ内で弱いシンボルの場合には、リンカーはそのライブラリからの定義を使用します。しかし、プログラムが独自のバージョンの symbol1 を定義している場合は、プログラムでの定義が使用され、ライブラリ内の symbol1 の弱い大域定義は使用されません。プログラムが symbol2 を直接呼び出す場合は、ライブラリからの定義が使用されます。symbol2 の重複定義は、エラーになります。