|
| 1 | +## ADDED Requirements |
| 2 | + |
| 3 | +### Requirement: A nested type is a module of its own, named by its owner |
| 4 | + |
| 5 | +Every class, record and struct declared inside another type SHALL have a twin of its own, in a module |
| 6 | +of its own, named by the chain of the types that contain it and its own name joined by `$` |
| 7 | +(`Cart$Item`), which no C# type can take. Every reference to it, inside its owner or out, SHALL name |
| 8 | +that twin: a construction, a type test, a static member, a default and an annotation alike. A nested |
| 9 | +type of an owner that never crosses (`[ServerOnly]`, an exception, an attribute) SHALL have none, and |
| 10 | +client code that reaches its twin SHALL be refused: one that builds it, tests for it, reads its statics, |
| 11 | +takes its zero or calls an operator or a conversion its value brings, however the code names the type |
| 12 | +(an alias, `using static`, inference). A module that would import such a twin SHALL report it rather |
| 13 | +than import it. A nested type of an owner the runtime provides SHALL be the runtime's, |
| 14 | +imported from it under its twin name. A record's text SHALL print its C# name, as .NET's does. The server SHALL name the page it serves by the |
| 15 | +same rule, so a page declared inside a class loads the module the build wrote for it, and SHALL refuse to |
| 16 | +map a page declared inside an owner that never crosses. |
| 17 | + |
| 18 | +#### Scenario: A nested class beside a top-level class of its name |
| 19 | + |
| 20 | +- **WHEN** `class Item { public int Qty = 3; }` and `class Cart { private class Item { public int Qty = 9; } public int Size() => new Item().Qty; }` |
| 21 | +- **THEN** `new Cart().Size()` is `9`, as in .NET |
| 22 | + |
| 23 | +#### Scenario: A private nested class built by its owner |
| 24 | + |
| 25 | +- **WHEN** `class Roster { private sealed class Row { public string Name = ""; } public static string First() { var r = new Row { Name = "Ada" }; return r.Name; } }` |
| 26 | +- **THEN** `Roster.First()` is `Ada`, as in .NET |
| 27 | + |
| 28 | +#### Scenario: A nested record beside a top-level record of its name |
| 29 | + |
| 30 | +- **WHEN** `class Shop { public record Line(int Qty); }` and `record Line(string Text);` |
| 31 | +- **THEN** `new Shop.Line(2).Qty` is `2`, `new Line("x").Text` is `x`, and `new Shop.Line(2)` prints `Line { Qty = 2 }`, as in .NET |
| 32 | + |
| 33 | +#### Scenario: A nested static class of a plain class |
| 34 | + |
| 35 | +- **WHEN** `class Calc { static class Ops { public static int Twice(int x) => x * 2; } public int Run(int x) => Ops.Twice(x); }` |
| 36 | +- **THEN** `new Calc().Run(4)` is `8`, as in .NET |
| 37 | + |
| 38 | +#### Scenario: A public nested type from outside its owner |
| 39 | + |
| 40 | +- **WHEN** `class Outer { public class Inner { public int N = 4; } }` and `object o = new Outer.Inner();` |
| 41 | +- **THEN** `((Outer.Inner)o).N` is `4` and `o is Outer.Inner` is true, as in .NET |
| 42 | + |
| 43 | +#### Scenario: Two levels of nesting |
| 44 | + |
| 45 | +- **WHEN** `class A { public class B { public class C { public int V = 1; } } }` |
| 46 | +- **THEN** `new A.B.C().V` is `1`, as in .NET |
| 47 | + |
| 48 | +#### Scenario: A nested type of a server-only owner |
| 49 | + |
| 50 | +- **WHEN** `[ServerOnly] class Vault { public class Key { } }` |
| 51 | +- **THEN** the build writes no module for `Key`, as it writes none for `Vault`, client code that builds a |
| 52 | + `Vault.Key` or tests for one is refused with EQ2010, and a page declared inside `Vault` has no route, |
| 53 | + and mapping it with `MapPage` fails at startup |
| 54 | + |
| 55 | +#### Scenario: A kept-out nested type the syntax never names |
| 56 | + |
| 57 | +- **WHEN** `[ServerOnly] class Vault` holds `Key`, `Pair` and `Amount`, and client code reads `K.Count` through |
| 58 | + `using K = Vault.Key;`, reads `Count` or calls `Make()` through `using static Vault.Key;`, asks for |
| 59 | + `default(Vault.Pair)`, declares a field of type `Vault.Pair`, or adds or converts a `Vault.Amount` that |
| 60 | + another class's method returns |
| 61 | +- **THEN** each is refused with EQ2010, and no module imports a twin of `Vault`'s |
| 62 | + |
| 63 | +#### Scenario: A nested type of a runtime-provided owner |
| 64 | + |
| 65 | +- **WHEN** `[RuntimeProvided] class Kit { public class Part { } }` and client code builds a `Kit.Part` |
| 66 | +- **THEN** the module imports `Kit$Part` from the runtime, which carries it as it carries |
| 67 | + `CodeBlock$CodeMetrics`, and the build writes no module for it |
| 68 | + |
| 69 | +#### Scenario: A page declared inside a class |
| 70 | + |
| 71 | +- **WHEN** `public static class Admin { [Page("/admin/users")] public class Users : StatelessComponent { … } }` is served |
| 72 | +- **THEN** the page's configuration and its route name `Admin$Users`, the module the build wrote |
| 73 | + |
| 74 | +#### Scenario: A nested component, tested and built by its twin |
| 75 | + |
| 76 | +- **WHEN** `class Host { public class Page : StatelessComponent { … } }`, `object o = new Host.Page();` and `Host.Page p = new();` |
| 77 | +- **THEN** `o is Host.Page` tests `Host$Page`, and both constructions build `Host$Page`, imported where it is used |
| 78 | + |
| 79 | +#### Scenario: A component beside a nested class of its name |
| 80 | + |
| 81 | +- **WHEN** `class Item : StatelessComponent { … }` and `class Cart { public class Item { } }` |
| 82 | +- **THEN** `Item` is the component's module and `Cart$Item` a plain class's, neither taken for the other |
| 83 | + |
| 84 | +#### Scenario: A nested type the syntax never names |
| 85 | + |
| 86 | +- **WHEN** `Factory.Make() + Factory.Make()`, `-Factory.Make()` and `total += Factory.Make()` bind the operators of a |
| 87 | + nested `Outer.Amount` that no expression names, and `default(Alias)` is the zero of a nested struct through |
| 88 | + `using Alias = Outer.Pair;` |
| 89 | +- **THEN** each module imports `Outer$Amount` and `Outer$Pair`, as it imports a twin the syntax names |
| 90 | + |
| 91 | +#### Scenario: A nested type inside an array or a generic |
| 92 | + |
| 93 | +- **WHEN** a member is typed `A.Inner[]`, `List<A.Inner>` or `Func<A.Inner, B.Inner>`, in a class or a record |
| 94 | +- **THEN** its annotation names `A$Inner` and `B$Inner`, each where its type stands, and a top-level `Inner` |
| 95 | + beside them keeps its own name |
0 commit comments