Skip to content
Merged
Show file tree
Hide file tree
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
12 changes: 11 additions & 1 deletion src/Sharpy.Cli.Tests/E2E/HostAssemblyVisibilityE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ namespace Sharpy.Cli.Tests.E2E;
/// SPY0300 at base 486c9a88a and compiled, then died with FileNotFoundException, at effba7776;
/// <c>from system.command_line import RootCommand</c> compiled then died (exit 134,
/// FileNotFoundException System.CommandLine) at both. A refusal (SPY0301, the system.* prefix's
/// answer for a namespace that exists nowhere) is the fix by direction. The
/// answer for a namespace that exists nowhere) is the fix by direction. An UNUSED import of a host
/// package (<c>from system.composition import ExportAttribute</c>, never referenced) compiled with
/// SPY0452 at base and is SPY0301 now: worked → rejected, under the owner's 2026-10-15 ruling on the
/// same mechanism (the unused <c>system.reactive.linq</c> cell on #2250) — the base answer depended on
/// which packages the host happened to ship, which is the contract's violation. The
/// <c>-r</c> cell is the positive control: the same import with the package given as a reference
/// compiles and runs.
/// </remarks>
Expand All @@ -34,6 +38,11 @@ public class HostAssemblyVisibilityE2ETests : IDisposable
"from system.command_line import RootCommand\n\n"
+ "def main() -> None:\n r = RootCommand(\"hello\")\n print(r.description)\n";

/// <summary>An unused host-package import: the owner-ruled worked → rejected direction (see remarks).</summary>
private const string CompositionUnusedProgram =
"from system.composition import ExportAttribute\n\n"
+ "def main() -> None:\n print(\"ok\")\n";

private readonly TempWorkspace _ws = new();

public void Dispose() => _ws.Dispose();
Expand All @@ -46,6 +55,7 @@ public class HostAssemblyVisibilityE2ETests : IDisposable
[Theory]
[InlineData("extensions_primitives", PrimitivesProgram, "SPY0300")]
[InlineData("command_line", CommandLineProgram, "SPY0301")]
[InlineData("composition_unused", CompositionUnusedProgram, "SPY0301")]
public void HostPackageImport_WithoutReference_IsRefusedAtImport(string cell, string program, string refusal)
{
_ws.WriteSpy(program);
Expand Down
331 changes: 315 additions & 16 deletions src/Sharpy.Compiler.Tests/Conformance/InProcessIsolationCorpus.cs

Large diffs are not rendered by default.

236 changes: 184 additions & 52 deletions src/Sharpy.Compiler.Tests/Conformance/InProcessIsolationSweepTests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ public class ProcessWideResolutionScanTests
"assembly qualifier binds against the visible set by simple name and an unqualified name is " +
"searched in the visible set, so nothing binds against the process."),
["Discovery/AssemblyVisibility.cs::AssemblyVisibility.LoadFrameworkAssembly::Assembly.Load"] = (1,
"the one seam: Assembly.Load(AssemblyName) of a trusted-platform System.*/Microsoft.*/netstandard/" +
"mscorlib name (or a compiler dependency), once per process behind the framework Lazy — the " +
"default context's TPA binding, a process constant."),
"the one seam: Assembly.Load(AssemblyName) of a shared-framework name (a .dll in the " +
"Microsoft.NETCore.App runtime directory CoreLib was loaded from) or Sharpy.Core, once per " +
"process behind the framework Lazy — the default context's TPA binding, a process constant."),
["Discovery/AssemblyVisibility.cs::AssemblyVisibility.IsProcessStable::AssemblyLoadContext.Default"] = (1,
"the one seam: Design Decision 3's gate — a static Type-keyed cache holds a type only when it is " +
"wholly in the default context, so no static cache pins a per-registry load context."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void KnownRed_RowsNameRealCellsAndCiteTheIssue()
foreach (var (cell, reason) in KnownRed)
{
Assert.Contains(cell, AllCells);
Assert.Contains("#2250", reason, StringComparison.Ordinal);
Assert.Matches(@"(^|\s)#\d+\b", reason);
}
Assert.True(AllCells.Count(c => !KnownRed.ContainsKey(c)) >= 2,
"each probe needs a green positive-control cell; the oracle is not 'always red'");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# In-process isolation sweep allowlist (P54, #2250) — read by InProcessIsolationSweepTests.
#
# One row per red cell: `program sharing threads operation bucket [probabilistic] # #2250 reason; reproduced k/R @ sha`
# One row per red cell: `program sharing threads operation bucket [probabilistic] # #NNNN reason; reproduced k/R @ sha`
# program a corpus id (InProcessIsolationCorpus: a named roster id or a fixture test name)
# sharing compiler-per-thread | api-per-thread | api-shared
# threads 1 | 2 | 8
Expand All @@ -10,4 +10,5 @@
#
# A red cell without a row fails. A row whose cell was exercised and reproduced in no repetition fails
# as stale — drain it when the fix lands — unless it is marked `probabilistic` (reported, not judged).
# Every row cites #2250; the k/R is the measured reproduction count at the named sha.
# Every row cites an OPEN issue by number (check_allowlist_issue_state.sh gates the state); the k/R is the measured
# reproduction count at the named sha.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public void ConcurrentWriters_AtOneClockTick_EachCrashGetsItsOwnDirectoryAndRepo
public void KnownRedRows_CiteTheIssue()
{
foreach (var (_, reason) in KnownRed)
Assert.Contains("#2250", reason, StringComparison.Ordinal);
Assert.Matches(@"(^|\s)#\d+\b", reason);
}

private static Outcome RunWriters(int writers, string outputDirectory)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,10 @@ public StaticStateConformanceTests(ITestOutputHelper output)

// ---- The visibility seam's framework index (#2250, P54 Design Decision 1) ----
[("Sharpy.Compiler.Discovery.AssemblyVisibility", "s_framework")] =
"Lazy<TypeIndex> with no key: the framework set — the trusted-platform System.*/Microsoft.*/" +
"netstandard/mscorlib assemblies, Sharpy.Core, the compiler and its direct dependencies — loaded " +
"and indexed once; the trusted-platform list is fixed at process start, so the value is a process " +
"constant. ExecutionAndPublication: a reader blocks until the index is complete. The five " +
"Lazy<TypeIndex> with no key: the framework set — the shared framework (every trusted-platform " +
"assembly whose .dll lies in the Microsoft.NETCore.App runtime directory CoreLib was loaded from) " +
"plus Sharpy.Core, never the host's own application packages (f5fb7f8e4) — loaded and indexed " +
"once; CoreLib's directory is fixed at process start, so the value is a process constant. ExecutionAndPublication: a reader blocks until the index is complete. The five " +
"name-keyed rows it replaced (_originNameDefinitionCache, _displayNameDefinitionCache, " +
"s_typePresentCache, _probedNamespaces, _loadedRuntimeNamespaces) drained into per-visibility state.",
};
Expand Down
4 changes: 2 additions & 2 deletions src/Sharpy.Compiler/CodeGen/RoslynEmitter.CompilationUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,8 @@ private static string ResolveClrTypeName(Symbol? symbol, string fallbackName)
/// </summary>
private static bool IsClrTypeGenericInNamespace(string namespaceName, string typeName)
{
// Rule-2 cell (#2250 findings ledger; no tracker yet): this is the emitter asking reflection a
// semantic question, which belongs in semantic analysis as a materialized fact. Until it moves,
// TODO(#2320): Rule-2 cell — this is the emitter asking reflection a semantic question, which
// belongs in semantic analysis as a materialized fact (CodeGenInfo). Until it moves,
// it asks the FRAMEWORK visibility — a process constant — instead of every assembly the process
// has loaded; its only caller passes a .NET framework namespace (IsNetFrameworkNamespace).
var framework = Discovery.AssemblyVisibility.FrameworkOnly;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public void KnownRedRows_NameCellsOfTheMatrix()
{
Assert.True(cells.Contains(cell), $"KnownRed row '{cell}' names no cell of the matrix");
Assert.False(cell.Contains($" {Fresh} ", StringComparison.Ordinal), $"'{cell}': the fresh arm is the positive control; it carries no KnownRed row");
Assert.Contains("#2250", row.Row, StringComparison.Ordinal);
Assert.Matches(@"(^|\s)#\d+\b", row.Row);
Assert.Matches(@"\d+/\d+[^@]* @ [0-9a-f]{7,}", row.Row);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void EverySkippedOperation_NamesItsReason()
Exercisers.Where(e => e.NotApplicable.Count == 0).Select(e => e.Module).OrderBy(m => m, StringComparer.Ordinal));
}

/// <summary>Every KnownRed row names a real cell and cites #2250 with a measured reproduction.</summary>
/// <summary>Every KnownRed row names a real cell and cites an issue by number with a measured reproduction.</summary>
[Fact]
public void EveryKnownRedRow_IsARealCellCitingTheIssue()
{
Expand All @@ -130,7 +130,7 @@ public void EveryKnownRedRow_IsARealCellCitingTheIssue()
foreach (var (cell, row) in KnownRed)
{
Assert.Contains(cell, cells);
Assert.Contains("#2250", row.Evidence, StringComparison.Ordinal);
Assert.Matches(@"(^|\s)#\d+\b", row.Evidence);
Assert.Contains(" @ ", row.Evidence, StringComparison.Ordinal);
}
}
Expand Down
Loading