diff --git "a/text/0042-\330\252\330\265\330\257\331\212\330\261-\330\247\331\204\330\257\331\210\330\247\331\204\331\221-\330\250\330\247\330\263\331\205-\330\261\331\205\330\262.md" "b/text/0042-\330\252\330\265\330\257\331\212\330\261-\330\247\331\204\330\257\331\210\330\247\331\204\331\221-\330\250\330\247\330\263\331\205-\330\261\331\205\330\262.md" new file mode 100644 index 0000000..86bcd0c --- /dev/null +++ "b/text/0042-\330\252\330\265\330\257\331\212\330\261-\330\247\331\204\330\257\331\210\330\247\331\204\331\221-\330\250\330\247\330\263\331\205-\330\261\331\205\330\262.md" @@ -0,0 +1,121 @@ +- **عنوان المقترح:** تصدير الدوالّ باسم رمز — `@رمز("اسم")` على تصريح دالّة +- **النطاق:** لغة `text/` +- **تاريخ البدء:** 2026-07-21 +- **رقم الـ RFC:** 0042 +- **الحالة:** مقترَح +- **Issue التتبُّع:** (يُترك فارغاً حتى القبول) + +# ملخّص + +نمدّ التوجيه القائم `@رمز("اسم")` — الذي يُصدِّر **متغيّرًا/ثابتًا** تحت اسم +رمز ASCII محدَّد (اللبنتان 3.14/3.16) — ليقبل **تصريح دالّة** كذلك. فتصير +`@رمز("bee_x") دالة اسم(...)` تُصدِّر الدالّة في LLVM باسم `bee_x` بربط +خارجيّ، بدل اسمها الداخليّ المُشوَّه — فيستدعيها كود C أو الكود الذي يولّده +المترجم بالاسم حرفيًّا. + +المبدأ الحاكم نفسه: **الرمز عقد ABI مع العالم الخارجيّ، والاسم العربيّ يبقى +لغة ص.** + +# الدافع (Motivation) + +- **إطفاء آخر تعريفات C في نواة النحلة:** المترجم يولّد نداءات إلى رموز + ABI محجوزة (`__sad_panic` لمسارات حدود المصفوفات، `__stack_chk_fail` + لفشل حارس الإطار). اليوم تُعرَّف هذه في C (`تراب.c`) لأنّ ص لا تملك + تصدير دالّة تحت اسم ASCII — فيتعذّر بلوغ هدف «صفر C». هذا التوجيه يزيل + العائق: تُعرَّف الدالّة بلغة ص وتُصدَّر تحت الاسم المحجوز. +- **تناظر مع `@رمز` على البيانات:** تصدير البيانات تحت اسم رمز (3.14) + وتصدير الكود تحته وجهان لعقد ABI واحد؛ إبقاء أحدهما دون الآخر تفاوت + تعسّفيّ. +- **الاستضافة الذاتيّة والـFFI العكسيّ:** أيّ سيناريو يستدعي فيه كود + خارجيّ (نظام تشغيل، مكتبة C، مُحمِّل) دالّةً مكتوبة بلغة ص يحتاج اسم + رمز مستقرًّا معلومًا. + +# الشرح التوجيهي (Guide-level explanation) + +`@رمز("اسم")` سمة سابقة على تصريح دالّة. تجعل رمز الرابط للدالّة = `"اسم"` +حرفيًّا (بدل الاسم الداخليّ)، بربط خارجيّ: + +```sad +# تُصدَّر في LLVM كـ: define void @__sad_panic(i64 %الرمز) { … } +@رمز("__sad_panic") دالة لا_ترجع اهلع_المترجم(رقم الرمز) + اطبع_خام("هلع") + قف_أبديًّا() +نهاية +``` + +- الدالّة **معرَّفة كاملةً** (لها جسم) لا مجرّد إعلان — بخلاف `دالة خارجية` + التي تستورد رمزًا بلا جسم. +- تتركّب مع المعدِّلات القائمة: `@رمز("s") دالة لا_ترجع اسم()` ⇒ رمز مُصدَّر + بسمة `noreturn` (3.15). +- الاسم العربيّ (`اهلع_المترجم`) يبقى للاستدعاء من كود ص؛ الاسم ASCII + للعالم الخارجيّ. (اليوم إن اختُلف الاسمان يُصدَّر بالاسم ASCII فقط؛ + الاستدعاء الداخليّ بلغة ص يمرّ عبر رمز الرابط نفسه.) + +# التصميم التفصيليّ (Reference-level explanation) + +الآليّة قائمة بالكامل تحت السطح؛ ينقصها ربط المحلّل فقط: + +1. **AST:** `FunctionDecl` يملك أصلًا `linkName` (كان لاستيراد FFI عبر + `دالة خارجية("اسم")`). نعيد استعماله للتصدير — دلالته الموحَّدة «اسم + رمز الرابط لهذه الدالّة». +2. **المحلّل** (`parser_main.cpp`، كتلة `@رمز`): بعد تحليل التصريح التالي، + إن كان `FunctionDecl` يُضبَط `linkName = اسم_الرمز` (كما يُضبَط + `linkSymbol` على `VarDeclStmt`). رسالة الخطأ تُحدَّث لتشمل «دالة». +3. **جسر SIR:** `funcDecl->linkName → sirFunction->linkName` يتدفّق أصلًا. +4. **الخلفيّة:** اسم رمز LLVM = `sirFunc->getLinkName()` (=‏`linkName` + إن وُجد) أصلًا؛ والدالّة ذات `linkName ≠ name` تبقى `ExternalLinkage` + (لا تُدوَّل). فالتصدير يعمل بلا تغيير خلفيّ. + +5. **حارس التصادم (SEM024):** إبقاء رمزين مُصدَّرين باسم رابط واحد على + تعريفين **مُعرَّفين** يجعل الخلفيّة تُفسدهما صامتًا: دالّتان تُدمَجان (جسم + الثانية يُلحَق ككتلة بلا سلف ويُفقَد)، ودالّة تشارك متغيّرًا الاسمَ تُعيد + LLVM تسمية أحدهما (`@اسم.1`) فيَضيع عقد الـABI — كلاهما كودٌ خاطئ صامت. لذا + يتتبّع الباني أسماء الرموز المُصدَّرة (مجموعتان: دالّة/متغيّر، متقاطعتان + مستقلًّا عن ترتيب المعالجة) ويرفض التكرار بخطأ قاتل `SEM024` لحالتَي + دالّة/دالّة ودالّة/متغيّر. (تصادم متغيّر/متغيّر يبقى لحارسه القائم خلفيًّا؛ + والدوالّ الخارجيّة `دالة خارجية` بلا جسم = استيراد، فلا تتصادم فتُستثنى.) + المجموعتان تُمسحان عند دخول كلّ وحدة (لئلّا يُطلَق `SEM024` كاذبًا عبر + الوحدات في بانٍ مُعاد استخدامه — LSP). + +**التغيير الصافي:** فرع `else if` واحد في المحلّل + حارس تصادم دلاليّ +(`SEM024`، تتبّع أسماء الرموز المُصدَّرة) + رسالة الخطأ. لا كلمة مفتاحيّة +جديدة، لا تغيير خلفيّ. + +## القيود (الإصدار الأوّل) + +- الدالّة المُصدَّرة يجب أن تكون بتوقيع بسيط قابل للتعبير في ABI الهدف + (لا أنواع عامّة/قوالب، لا async/مولّد — تلك تُشوّه أسماءها أو تحتاج بيئة + تشغيل). الاستعمال الأوّل (نواة، FFI عكسيّ) لا يحتاج غير التوقيع البسيط. +- مسؤوليّة تطابق التوقيع مع عقد المستدعي الخارجيّ على المبرمج (نظير + `دالة خارجية`). + +# البدائل (Alternatives) + +- **حقل `exportName` مستقلّ عن `linkName`:** أوضح دلاليًّا لكن يضاعف مسارات + الخلفيّة بلا كسب (كلاهما «اسم رمز الرابط»). رُفض لصالح إعادة الاستعمال. +- **كلمة مفتاحيّة `صدّر_كـ("اسم")`:** تركيب جديد بلا داعٍ؛ `@رمز` قائم + ومفهوم ومتناظر مع البيانات. +- **إبقاء `__sad_panic` في C أبدًا:** يناقض «صفر C» ويترك دَينًا دائمًا. + +# أسئلة مفتوحة (Unresolved questions) + +- تصدير دالّة عامّة/مُنمَّطة (بأيّ تخصيص؟) — مؤجَّل حتّى يظهر طلب فعليّ. +- حارس دلاليّ يرفض `@رمز` على async/مولّد/عامّة برسالة صريحة — تحسين + لاحق (الإصدار الأوّل يعتمد على بساطة الاستعمال؛ رصدته المراجعة العدائيّة + كفخّ بلا تشخيص M3). أمّا **تصادم اسم الرمز المكرَّر فمحروس منذ الإصدار + الأوّل** (`SEM024`) — دالّة/دالّة ودالّة/متغيّر معًا — بعد أن كشفته المراجعة + العدائيّة (دمج/إعادة تسمية صامتة ⇒ كودٌ خاطئ). +- تصدير `@رمز("malloc"/"free"/"memset"/…)` يتجاوز رمزًا يوفّره زمن التشغيل + (weak): مسار الخلفيّة يفرض نوع العائد لهذه الأسماء المعروفة، فقد يُبدَّل + عائد الدالّة المُصدَّرة صامتًا (رصد المراجعة العدائيّة M2). مقبولٌ في الإصدار + الأوّل (نظير `دالة خارجية`: تطابق التوقيع عهدةٌ على المبرمج)؛ حارس أسماء + محجوزة للدوالّ تحسينٌ لاحق. +- تجاوز مدمج بتوقيع مخالف (`@رمز("__sad_panic") دالة بلا معاملات`) يبقى + مسؤوليّة المبرمج (نظير `دالة خارجية`) — تطابق التوقيع مع عقد المستدعي + عهدةٌ عليه. + +# الأثر على مصدر الحقيقة + +لا شيء: `@رمز` توجيه سمة لا كلمة مفتاحيّة، فلا إدخال في `keywords.yaml` ولا +توليد. اختبارات منع الانحدار في +`tests/system/lowlevel_freestanding/test_symbol_export_func.py`.