Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions text/0000-عمر-متغيّر-نمط-المطابقة.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
- **عنوان المقترح:** عمر متغيّر نمط المطابقة ودلالة التظليل — قصرٌ صارم على الذراع في المحرّكين
- **النطاق:** لغة `text/`
- **تاريخ البدء:** 2026-07-07
- **رقم الـ RFC:** (يُترك فارغاً حتى الدمج)
- **الحالة:** مقترَح (PR مفتوح)
- **Issue التتبُّع:** (يُترك فارغاً حتى القبول — سيُتتبَّع في مستودع الكود كـISSUE-079)

# ملخّص

عند استعمال اسمٍ مربوطٍ بنمط `طابق` **بعد** انتهاء الجملة، يتباعد محرّكا لغة ص (المفسّر `sad-run` والمترجم `sad-build`) — وهو صنف التباعد الأخطر في المشروع (نتيجةٌ مختلفة صامتة بين المحرّكين). التحقيق الحيّ (٨ حالات) كشف أنّ المشكلة **دلاليّة لغويّة غير محسومة في مصدر الحقيقة** (`language-truth/` صامتٌ تمامًا عن عمر ربط متغيّرات النمط والتظليل)، لا علّة مترجمٍ أحاديّة.

يقترح هذا الـRFC حسمًا صريحًا: **متغيّر النمط يُقصَر على ذراعه، ويُظلِّل أيّ متغيّرٍ خارجيّ بنفس الاسم دون إعادة إسناده، ويموت بانتهاء الذراع** (يوافق Rust وML وSwift). ثمّ توحيد المحرّكين على هذه الدلالة.

# الدافع (Motivation)

مثالٌ حيّ يُظهر التباعد الصامت (متغيّرٌ خارجيّ يحمل اسم متغيّر النمط):

```sad
تعداد نتيجة
نجاح(عدد)
فشل
نهاية
متغير ق = 100
متغير م = [نتيجة.نجاح(42)]
طابق (م)
عندما [نتيجة.نجاح(ق)]: اطبع_سطر("داخل=" + نص(ق))
افتراضي: اطبع_سطر("لا")
نهاية
اطبع_سطر("بعد=" + نص(ق))
```

- **المفسّر:** `داخل=42` ثمّ `بعد=42` — يُعيد إسناد `ق` الخارجيّ (لا يُظلّله).
- **المترجم:** `داخل=42` ثمّ **«LLVM CodeGen Error: Undefined register»** و`بعد=0` (خروج 0 = نجاح صامت خاطئ).

نتيجتان مختلفتان لبرنامجٍ واحد = خرقٌ لمبدأ التكافؤ (المحرّكان يجب أن يتطابقا). ومصدر الحقيقة لا يُملي أيّ السلوكين صحيح، فالقرار غير محسوم ويجب حسمه لغويًّا قبل أيّ إصلاحٍ في المحرّكين (الأدوات/المترجم لا تُوسّع ولا تحسم دلالة اللغة — هذا قرار SoT حصرًا).

# الشرح التوجيهي (Guide-level explanation)

متغيّر النمط في `عندما` هو **ربطٌ جديدٌ محلّيّ لذراعه**: يعيش داخل جسم الذراع فقط، ويُظلِّل أيّ متغيّرٍ خارجيّ يحمل الاسم نفسه دون تغييره، ويختفي بمجرّد انتهاء الذراع.

```sad
متغير ق = 100
طابق (م)
عندما [نتيجة.نجاح(ق)]: اطبع_سطر(نص(ق)) # ق هنا = الحمولة (42)، تظليلٌ محلّيّ
افتراضي: اطبع_سطر("لا")
نهاية
اطبع_سطر(نص(ق)) # ق هنا = 100 (الخارجيّ سليم، لم يُمَسّ)
```

ومتغيّرٌ طازج (غير مُصرَّح قبل المطابقة) لا يكون معرَّفًا بعد المطابقة:

```sad
طابق (م)
عندما نتيجة.نجاح(ك): اطبع_سطر(نص(ك)) # ك معرَّف داخل الذراع
نهاية
اطبع_سطر(نص(ك)) # ⛔ خطأ: 'ك' غير معرَّف (SEM001)
```

هذه القاعدة مألوفةٌ لمن جرّب مطابقة الأنماط في Rust/Swift/ML، وتمنع «تسريب» متغيّرات النمط الّذي يُربك القارئ ويُخفي أخطاءً.

# الشرح المرجعي (Reference-level explanation)

## الدلالة المقترَحة (موحَّدة على المحرّكين)
1. كلّ متغيّرٍ يظهر في نمطِ `عندما` يُنشئ **ربطًا جديدًا** في نطاقٍ خاصٍّ بجسم ذلك الذراع (وحارسه `إذا` إن وُجد).
2. هذا الربط **يُظلِّل** أيّ متغيّرٍ خارجيّ بنفس الاسم؛ **لا يُعيد إسناده**.
3. الربط **يموت** بانتهاء الذراع؛ استعماله بعد `نهاية` المطابقة = «غير معرَّف» (إلّا إن كان مُصرَّحًا خارجيًّا سلفًا، فيعود الخارجيّ سليمًا).

## الوضع الراهن (من التحقيق الحيّ — ٨ حالات)
- **المفسّر متّسق داخليًّا** بسلوكٍ مزدوجٍ محكومٍ بجذرٍ واحد: `VariableManager::define` **يصعد إلى النطاق الأب ويُحدّث** المتغيّر الموجود بدل تظليله. لذا الطازج يُقصَر ويموت (SEM001)، والمُظلِّل **يُعيد إسناد الخارجيّ**. هذا الأخير **ليس سلوكًا خاصًّا بالمطابقة** بل انعكاسٌ لعلّة نطاق الكتل العامّة المسجَّلة كـ**ISSUE-028**.
- **المترجم غير متّسق** — ثلاثة سلوكيّات حسب نوع النمط:
- **نمط متغيّر بسيط** (`عندما ك:`): الربط **يتسرّب** (يُربَط لسجلّ ما قبل المطابقة المُهيمِن على كتلة الدمج).
- **نمط ADT مباشر** (`عندما نتيجة.نجاح(ق)`): **مقصورٌ صحيحًا** — يعود الخارجيّ لقيمته دون خطأ. **هذا هو السلوك المعياريّ المقترَح، مُطبَّقٌ فعلًا هنا.**
- **نمط قائمة / تعداد داخل قائمة**: مقصورٌ لكنّ الإشارة بعد المطابقة تُفشِل البناء أو تنهار في codegen («Undefined register») مع خروج 0.

## الأثر على الأنظمة المتشابكة
- **مصدر الحقيقة (`language-truth/`):** إضافة **بندٍ دلاليّ صريح** يوثّق عمر متغيّر النمط ودلالة التظليل (لا كلمة/توجيه/نوع جديد؛ توثيق دلالةٍ قائمة). قد يستلزم رمز خطأٍ في `language-truth/errors/` إن لم يوجد نظير SEM001 لـ«استعمال متغيّر نمطٍ بعد ذراعه».
- **المحلل المعجمي/النحوي:** لا تغيير (الصياغة قائمة).
- **الشجرة المجردة (AST):** لا عقد جديدة.
- **المفسّر:** ربطٌ قسريٌّ **محلّيّ** لمتغيّرات النمط داخل `visitMatchStmt` (إنشاء بيئة/نطاق للذراع والربط فيها مباشرةً) **دون** المساس بـ`VariableManager::define` العامّ (كي لا نمسّ ISSUE-028 خارج المطابقة). بذلك يتوقّف «إعادة إسناد» الخارجيّ في سياق المطابقة.
- **المترجم (`sad-build`):** (١) وقف تسريب النمط المتغيّر البسيط (ربطه في نطاق جسم الذراع لا لسجلٍّ خارجيّ مُهيمِن على الدمج)؛ (٢) توحيد الإشارة بعد المطابقة على «غير معرَّف» (SEM001) بدل انهيار codegen؛ نمط ADT المباشر يبقى كما هو (يطبّق المعيار سلفًا).
- **نظام الأخطاء:** إعادة استعمال SEM001 «المتغير غير معرَّف» للاستعمال بعد الذراع في المحرّكين.
- **الأدوات:** LSP يستفيد (نطاق متغيّر النمط يصبح محدَّدًا بدقّة لإكمالٍ/تمييزٍ صحيح).
- **التوافق الخلفي:** قد يكسر برامج تعتمد **عن غير قصد** على تسرّب/إعادة-إسناد متغيّر النمط (نمطٌ غير اصطلاحيّ، نادر). يُخفَّف بأنّ الحالة الاصطلاحيّة (استعمالٌ داخل الذراع) لا تتأثّر البتّة.

# السلبيات (Drawbacks)

- تغييرُ سلوكٍ ملحوظٍ في المفسّر (إعادة الإسناد الحاليّة) قد يفاجئ من اعتمدها.
- الإصلاح يمسّ مسارات ربطٍ متعدّدة في المحرّكين (خطر انحدار)، ويستلزم تغطيةً اختباريّة مزدوجة دقيقة.
- «إعادة الإسناد» في المفسّر جذرها ISSUE-028 (نطاق الكتل العامّ)؛ حصرُ الإصلاح في المطابقة يترك ISSUE-028 قائمًا خارجها (ازدواجيّة مؤقّتة حتّى يُعالَج ISSUE-028 جذريًّا).

# المبرّرات والبدائل (Rationale and alternatives)

**الخيار (أ) — قصرٌ صارم للذراع بتظليلٍ بلا إعادة إسناد [المقترَح]:**
- المعيار الصناعيّ الغالب (Rust/Swift/ML/Haskell)؛ يمنع التسرّب والنجاح الصامت الخاطئ.
- **المترجم يطبّقه سلفًا لنمط ADT المباشر** ⇒ المطلوب **تعميمه** لا اختراعه.
- يجعل نطاق متغيّر النمط قابلًا للاستدلال محلّيًّا (أفضل لـLSP والقارئ).

**الخيار (ب) — توحيدٌ على «إعادة إسناد الخارجيّ» (كسلوك المفسّر الحاليّ):**
- يستلزم أن يخزّن المترجم متغيّر النمط في `alloca` الخارجيّ ويُعمّم التسريب — **عكس المعيار**، ويُبقي علّة ISSUE-028 دلالةً رسميّة، ويستلزم التراجع عن سلوكٍ صحيحٍ قائم (ADT). خطيرٌ لغويًّا.

**عدم فعل شيء:** يُبقي تباعدًا صامتًا (نتيجة مختلفة أو انهيار codegen صامت) — غير مقبول لمبدأ التكافؤ.

# أعمال سابقة (Prior art)

- **Rust:** متغيّرات نمط `match` مقصورةٌ على الذراع، تُظلِّل بلا إعادة إسناد، وتموت بعده. المعيار الأقرب لروح ص.
- **Swift / OCaml / Haskell / ML:** المثل — ربطٌ محلّيّ للنمط، لا تسريب.
- **Python 3.10 `match`:** الاستثناء الوحيد — يُسرِّب أسماء النمط للنطاق المحيط، لسببٍ بنيويّ (لا كتل نطاقٍ للدوال الفرعيّة) **لا ينطبق على ص** (التي تملك نطاقات كتل). لذا لا يُتّخذ Python مرجعًا هنا.

# أسئلة غير محسومة (Unresolved questions)

- هل يُعالَج ISSUE-028 (إعادة إسناد `define` الصاعد) جذريًّا الآن أم يُحصَر إصلاح المطابقة محلّيًّا أولًا؟ (المقترَح: الحصر المحلّيّ أولًا لتقليل الخطر.)
- هل نحتاج رمز خطأٍ متخصّصًا لـ«استعمال متغيّر نمطٍ خارج ذراعه» أم يكفي SEM001 العامّ؟
- سلوك متغيّر النمط داخل **حارس** `إذا` وداخل **حلقاتٍ/تداخلٍ** — يُثبَّت صراحةً باختبارات مزدوجة عند التنفيذ.

# إمكانات مستقبلية (Future possibilities)

- معالجة ISSUE-028 جذريًّا (تظليل كتل عامّ) بعد إثبات النموذج في المطابقة.
- تمكين أنماط الربط `@` (`باسم @ نمط`) بنطاقٍ متّسق فور استقرار دلالة العمر.
- تحسين LSP لإبراز نطاق متغيّر النمط بصريًّا.

---

> **مرجع تقنيّ:** تقرير التحقيق الحيّ (٨ حالات، مفسّر مقابل مترجم) موثّقٌ في خزنة تقارير الفريق، ومُلخَّصٌ في `language-truth/grammar/DISCOVERED_ISSUES.md` تحت **ISSUE-079**. هذا الـRFC نتيجةٌ مباشرةٌ لإصلاح ISSUE-078 (PR الكود #161) الّذي كشف التباعد أثناء المراجعة العدائيّة.
Loading