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
2,068 changes: 2,068 additions & 0 deletions Compilation/RuntimeEmitter.Coercion.cs

Large diffs are not rendered by default.

1,177 changes: 1,177 additions & 0 deletions Compilation/RuntimeEmitter.ConsoleHelpers.cs

Large diffs are not rendered by default.

4,454 changes: 0 additions & 4,454 deletions Compilation/RuntimeEmitter.CoreUtilities.cs

This file was deleted.

487 changes: 487 additions & 0 deletions Compilation/RuntimeEmitter.Objects.DeleteProperty.cs

Large diffs are not rendered by default.

5,194 changes: 1,531 additions & 3,663 deletions Compilation/RuntimeEmitter.Objects.Properties.cs

Large diffs are not rendered by default.

1,658 changes: 1,658 additions & 0 deletions Compilation/RuntimeEmitter.Objects.SetProperty.cs

Large diffs are not rendered by default.

1,218 changes: 1,218 additions & 0 deletions Compilation/RuntimeEmitter.Operators.cs

Large diffs are not rendered by default.

373 changes: 373 additions & 0 deletions Execution/Interpreter.Realm.cs

Large diffs are not rendered by default.

749 changes: 749 additions & 0 deletions Execution/Interpreter.Statements.cs

Large diffs are not rendered by default.

1,093 changes: 3 additions & 1,090 deletions Execution/Interpreter.cs

Large diffs are not rendered by default.

731 changes: 731 additions & 0 deletions Runtime/BuiltIns/ObjectBuiltIns.RuntimeDescriptors.cs

Large diffs are not rendered by default.

739 changes: 1 addition & 738 deletions Runtime/BuiltIns/ObjectBuiltIns.cs

Large diffs are not rendered by default.

873 changes: 873 additions & 0 deletions TypeSystem/BuiltInModuleTypes.NetworkStreams.cs

Large diffs are not rendered by default.

831 changes: 831 additions & 0 deletions TypeSystem/BuiltInModuleTypes.System.cs

Large diffs are not rendered by default.

1,708 changes: 3 additions & 1,705 deletions TypeSystem/BuiltInModuleTypes.cs

Large diffs are not rendered by default.

924 changes: 471 additions & 453 deletions TypeSystem/TypeChecker.Calls.cs

Large diffs are not rendered by default.

228 changes: 228 additions & 0 deletions TypeSystem/TypeChecker.Compatibility.Relations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
using SharpTS.TypeSystem.Exceptions;
using SharpTS.Runtime.BuiltIns;
using System.Collections.Frozen;
using SharpTS.Parsing;

namespace SharpTS.TypeSystem;

/// <summary>
/// Named relation helpers extracted from the leading sections of
/// <see cref="TypeChecker.IsCompatibleCore"/> (#1140). Each mirrors the existing
/// <c>TryRelateDeferredMappedType</c> shape: it returns <c>true</c> with
/// <c>result</c> set when it decides the relation for its type category, and
/// <c>false</c> (leaving the decision to the remaining rules) otherwise. Splitting
/// these out turns the top of the single sequential cascade into self-describing
/// units without changing evaluation order or behaviour.
/// </summary>
public partial class TypeChecker
{
/// <summary>
/// Type-predicate targets: a regular predicate <c>x is T</c> expects a boolean
/// return; an assertion predicate <c>asserts x is T</c> and a bare
/// <c>asserts x</c> expect void (the function throws on failure).
/// </summary>
private bool TryRelateTypePredicate(TypeInfo expected, TypeInfo actual, out bool result)
{
if (expected is TypeInfo.TypePredicate pred)
{
if (pred.IsAssertion)
{
// Assertion predicates return void (or throw)
result = actual is TypeInfo.Void or TypeInfo.Never;
return true;
}
else
{
// Regular type predicates return boolean
result = actual is TypeInfo.Primitive { Type: Parsing.TokenType.TYPE_BOOLEAN }
or TypeInfo.BooleanLiteral;
return true;
}
}
if (expected is TypeInfo.AssertsNonNull)
{
// AssertsNonNull returns void (or throws)
result = actual is TypeInfo.Void or TypeInfo.Never;
return true;
}
result = false;
return false;
}

/// <summary>
/// Type-parameter compatibility (TypeScript: type parameters are not assignable
/// to one another unless directly or indirectly constrained to one another);
/// an arbitrary concrete type is not assignable to a bare type parameter; and a
/// source type parameter is assignable wherever its constraint is.
/// </summary>
private bool TryRelateTypeParameters(TypeInfo expected, TypeInfo actual, out bool result)
{
// Type-parameter compatibility (TypeScript: "type parameters are not assignable to one
// another unless directly or indirectly constrained to one another").
if (expected is TypeInfo.TypeParameter expectedTp && actual is TypeInfo.TypeParameter actualTp)
{
// The same parameter, or a source transitively constrained to the target (U extends … extends T).
result = expectedTp.Name == actualTp.Name || TypeParameterConstrainedTo(actualTp, expectedTp.Name);
return true;
}

// Expected is a bare type parameter and the source is some other type. An arbitrary concrete
// type is NOT assignable to a type parameter — only `never`, or an intersection one of whose
// constituents is (T & Function → T). (any / inferred and, under non-strict, null / undefined
// are already accepted earlier in IsCompatibleCore; a source type parameter is handled by the
// case above.) This is the strict TypeScript rule.
if (expected is TypeInfo.TypeParameter)
{
if (actual is TypeInfo.Intersection actIntForTp)
result = actIntForTp.FlattenedTypes.Any(t => IsCompatible(expected, t));
else
result = actual is TypeInfo.Never;
return true;
}

// Source is a type parameter assigned to a non-parameter target: it is assignable wherever its
// apparent (constraint) type is assignable. Also assignable into a union that contains it.
if (actual is TypeInfo.TypeParameter actualTpOnly)
{
if (expected is TypeInfo.Any or TypeInfo.Unknown) { result = true; return true; }
if (expected is TypeInfo.Union expUnionForTp &&
expUnionForTp.FlattenedTypes.Any(t =>
t is TypeInfo.TypeParameter unionTp && unionTp.Name == actualTpOnly.Name))
{
result = true;
return true;
}
var apparent = ApparentTypeOf(actualTpOnly);
result = apparent != null && IsCompatible(expected, apparent);
return true;
}
result = false;
return false;
}

/// <summary>
/// The bottom/top/object trivial relations: <c>never</c> as source (assignable
/// to anything) or target (accepts only never); <c>unknown</c> as target (top)
/// or source; and the non-primitive <c>object</c> type in either position.
/// </summary>
private bool TryRelateNeverUnknownObject(TypeInfo expected, TypeInfo actual, out bool result)
{
// never as actual: assignable to anything (bottom type)
if (actual is TypeInfo.Never) { result = true; return true; }

// never as expected: nothing assignable to never except never
if (expected is TypeInfo.Never) { result = actual is TypeInfo.Never; return true; }

// unknown as expected: anything can be assigned TO unknown (top type)
if (expected is TypeInfo.Unknown) { result = true; return true; }

// unknown as actual: can only be assigned to unknown or any
if (actual is TypeInfo.Unknown)
{
result = expected is TypeInfo.Unknown || expected is TypeInfo.Any;
return true;
}

// object type: accepts non-primitive, non-null values
if (expected is TypeInfo.Object)
{
if (actual is TypeInfo.Never) { result = true; return true; } // never is bottom type
if (actual is TypeInfo.Any) { result = true; return true; } // any is assignable to anything
if (actual is TypeInfo.Object) { result = true; return true; } // object to object
if (IsPrimitiveType(actual)) { result = false; return true; } // reject primitives
if (actual is TypeInfo.Null or TypeInfo.Undefined) { result = false; return true; }
// Accept: Record, Array, Instance, Class, Function, Map, Set, etc.
result = true;
return true;
}

// object as actual: can only assign to object, any, unknown
if (actual is TypeInfo.Object)
{
result = expected is TypeInfo.Object or TypeInfo.Any or TypeInfo.Unknown;
return true;
}
result = false;
return false;
}

/// <summary>
/// Null / undefined as source under strictNullChecks (the non-strict case is
/// handled at the very top of IsCompatibleCore): assignable only to a matching
/// bare type or a union that includes null / undefined respectively.
/// </summary>
private bool TryRelateNullUndefinedStrict(TypeInfo expected, TypeInfo actual, out bool result)
{
// Null compatibility (strictNullChecks: on — the off case is handled early in IsCompatibleCore)
if (actual is TypeInfo.Null)
{
if (expected is TypeInfo.Union u && u.ContainsNull) { result = true; return true; }
if (expected is TypeInfo.Null) { result = true; return true; }
result = false;
return true;
}

// Undefined compatibility (strictNullChecks: on)
if (actual is TypeInfo.Undefined)
{
if (expected is TypeInfo.Union u && u.ContainsUndefined) { result = true; return true; }
if (expected is TypeInfo.Undefined) { result = true; return true; }
result = false;
return true;
}
result = false;
return false;
}

/// <summary>
/// Literal-type relations: literal-to-literal equality, literal-to-primitive
/// widening, template-literal pattern matching, and intrinsic string types.
/// Each arm decides only its specific shape and otherwise falls through.
/// </summary>
private bool TryRelateLiteralTypes(TypeInfo expected, TypeInfo actual, out bool result)
{
// Literal type compatibility - literal to literal (must have same value)
if (expected is TypeInfo.StringLiteral sl1 && actual is TypeInfo.StringLiteral sl2)
{ result = sl1.Value == sl2.Value; return true; }
if (expected is TypeInfo.NumberLiteral nl1 && actual is TypeInfo.NumberLiteral nl2)
{ result = nl1.Value == nl2.Value; return true; }
if (expected is TypeInfo.BooleanLiteral bl1 && actual is TypeInfo.BooleanLiteral bl2)
{ result = bl1.Value == bl2.Value; return true; }
if (expected is TypeInfo.BigIntLiteral bil1 && actual is TypeInfo.BigIntLiteral bil2)
{ result = bil1.Value == bil2.Value; return true; }

// Literal to primitive widening
if (expected is TypeInfo.String && actual is TypeInfo.StringLiteral)
{ result = true; return true; }
if (expected is TypeInfo.Primitive { Type: TokenType.TYPE_NUMBER } && actual is TypeInfo.NumberLiteral)
{ result = true; return true; }
if (expected is TypeInfo.Primitive { Type: TokenType.TYPE_BOOLEAN } && actual is TypeInfo.BooleanLiteral)
{ result = true; return true; }
if (expected is TypeInfo.BigInt && actual is TypeInfo.BigIntLiteral)
{ result = true; return true; }

// Template literal type compatibility

// Template literal widens to string
if (expected is TypeInfo.String && actual is TypeInfo.TemplateLiteralType)
{ result = true; return true; }

// String literal matches template literal pattern
if (expected is TypeInfo.TemplateLiteralType expectedTL && actual is TypeInfo.StringLiteral actualSL)
{ result = MatchesTemplateLiteralPattern(expectedTL, actualSL.Value); return true; }

// Template literal to template literal: structural compatibility
if (expected is TypeInfo.TemplateLiteralType expTL && actual is TypeInfo.TemplateLiteralType actTL)
{ result = TemplatePatternStructurallyCompatible(expTL, actTL); return true; }

// Intrinsic string type: evaluate and check
if (actual is TypeInfo.IntrinsicStringType ist)
{
var evaluated = EvaluateIntrinsicStringType(ist.Inner, ist.Operation);
result = IsCompatible(expected, evaluated);
return true;
}
result = false;
return false;
}
}
147 changes: 5 additions & 142 deletions TypeSystem/TypeChecker.Compatibility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -506,152 +506,15 @@ private bool IsCompatibleCore(TypeInfo expected, TypeInfo actual)
&& IsCompatible(targetCond.FalseType, actual);
}

// Type predicate compatibility:
// - Regular type predicate (x is T): expects boolean return
// - Assertion type predicate (asserts x is T): expects void return (function throws if assertion fails)
// - AssertsNonNull (asserts x): expects void return
if (expected is TypeInfo.TypePredicate pred)
{
if (pred.IsAssertion)
{
// Assertion predicates return void (or throw)
return actual is TypeInfo.Void or TypeInfo.Never;
}
else
{
// Regular type predicates return boolean
return actual is TypeInfo.Primitive { Type: Parsing.TokenType.TYPE_BOOLEAN }
or TypeInfo.BooleanLiteral;
}
}
if (expected is TypeInfo.AssertsNonNull)
{
// AssertsNonNull returns void (or throws)
return actual is TypeInfo.Void or TypeInfo.Never;
}

// Type-parameter compatibility (TypeScript: "type parameters are not assignable to one
// another unless directly or indirectly constrained to one another").
if (expected is TypeInfo.TypeParameter expectedTp && actual is TypeInfo.TypeParameter actualTp)
{
// The same parameter, or a source transitively constrained to the target (U extends … extends T).
return expectedTp.Name == actualTp.Name || TypeParameterConstrainedTo(actualTp, expectedTp.Name);
}

// Expected is a bare type parameter and the source is some other type. An arbitrary concrete
// type is NOT assignable to a type parameter — only `never`, or an intersection one of whose
// constituents is (T & Function → T). (any / inferred and, under non-strict, null / undefined
// are already accepted earlier in IsCompatibleCore; a source type parameter is handled by the
// case above.) This is the strict TypeScript rule.
if (expected is TypeInfo.TypeParameter)
{
if (actual is TypeInfo.Intersection actIntForTp)
return actIntForTp.FlattenedTypes.Any(t => IsCompatible(expected, t));
return actual is TypeInfo.Never;
}

// Source is a type parameter assigned to a non-parameter target: it is assignable wherever its
// apparent (constraint) type is assignable. Also assignable into a union that contains it.
if (actual is TypeInfo.TypeParameter actualTpOnly)
{
if (expected is TypeInfo.Any or TypeInfo.Unknown) return true;
if (expected is TypeInfo.Union expUnionForTp &&
expUnionForTp.FlattenedTypes.Any(t =>
t is TypeInfo.TypeParameter unionTp && unionTp.Name == actualTpOnly.Name))
{
return true;
}
var apparent = ApparentTypeOf(actualTpOnly);
return apparent != null && IsCompatible(expected, apparent);
}

// never as actual: assignable to anything (bottom type)
if (actual is TypeInfo.Never) return true;

// never as expected: nothing assignable to never except never
if (expected is TypeInfo.Never) return actual is TypeInfo.Never;

// unknown as expected: anything can be assigned TO unknown (top type)
if (expected is TypeInfo.Unknown) return true;

// unknown as actual: can only be assigned to unknown or any
if (actual is TypeInfo.Unknown)
return expected is TypeInfo.Unknown || expected is TypeInfo.Any;
if (TryRelateTypePredicate(expected, actual, out var predicateRel)) return predicateRel;

// object type: accepts non-primitive, non-null values
if (expected is TypeInfo.Object)
{
if (actual is TypeInfo.Never) return true; // never is bottom type
if (actual is TypeInfo.Any) return true; // any is assignable to anything
if (actual is TypeInfo.Object) return true; // object to object
if (IsPrimitiveType(actual)) return false; // reject primitives
if (actual is TypeInfo.Null or TypeInfo.Undefined) return false;
// Accept: Record, Array, Instance, Class, Function, Map, Set, etc.
return true;
}
if (TryRelateTypeParameters(expected, actual, out var typeParamRel)) return typeParamRel;

// object as actual: can only assign to object, any, unknown
if (actual is TypeInfo.Object)
{
return expected is TypeInfo.Object or TypeInfo.Any or TypeInfo.Unknown;
}

// Null compatibility (strictNullChecks: on — the off case is handled early in IsCompatibleCore)
if (actual is TypeInfo.Null)
{
if (expected is TypeInfo.Union u && u.ContainsNull) return true;
if (expected is TypeInfo.Null) return true;
return false;
}

// Undefined compatibility (strictNullChecks: on)
if (actual is TypeInfo.Undefined)
{
if (expected is TypeInfo.Union u && u.ContainsUndefined) return true;
if (expected is TypeInfo.Undefined) return true;
return false;
}
if (TryRelateNeverUnknownObject(expected, actual, out var neverUnknownObjectRel)) return neverUnknownObjectRel;

// Literal type compatibility - literal to literal (must have same value)
if (expected is TypeInfo.StringLiteral sl1 && actual is TypeInfo.StringLiteral sl2)
return sl1.Value == sl2.Value;
if (expected is TypeInfo.NumberLiteral nl1 && actual is TypeInfo.NumberLiteral nl2)
return nl1.Value == nl2.Value;
if (expected is TypeInfo.BooleanLiteral bl1 && actual is TypeInfo.BooleanLiteral bl2)
return bl1.Value == bl2.Value;
if (expected is TypeInfo.BigIntLiteral bil1 && actual is TypeInfo.BigIntLiteral bil2)
return bil1.Value == bil2.Value;
if (TryRelateNullUndefinedStrict(expected, actual, out var nullUndefinedRel)) return nullUndefinedRel;

// Literal to primitive widening
if (expected is TypeInfo.String && actual is TypeInfo.StringLiteral)
return true;
if (expected is TypeInfo.Primitive { Type: TokenType.TYPE_NUMBER } && actual is TypeInfo.NumberLiteral)
return true;
if (expected is TypeInfo.Primitive { Type: TokenType.TYPE_BOOLEAN } && actual is TypeInfo.BooleanLiteral)
return true;
if (expected is TypeInfo.BigInt && actual is TypeInfo.BigIntLiteral)
return true;

// Template literal type compatibility

// Template literal widens to string
if (expected is TypeInfo.String && actual is TypeInfo.TemplateLiteralType)
return true;

// String literal matches template literal pattern
if (expected is TypeInfo.TemplateLiteralType expectedTL && actual is TypeInfo.StringLiteral actualSL)
return MatchesTemplateLiteralPattern(expectedTL, actualSL.Value);

// Template literal to template literal: structural compatibility
if (expected is TypeInfo.TemplateLiteralType expTL && actual is TypeInfo.TemplateLiteralType actTL)
return TemplatePatternStructurallyCompatible(expTL, actTL);

// Intrinsic string type: evaluate and check
if (actual is TypeInfo.IntrinsicStringType ist)
{
var evaluated = EvaluateIntrinsicStringType(ist.Inner, ist.Operation);
return IsCompatible(expected, evaluated);
}
if (TryRelateLiteralTypes(expected, actual, out var literalRel)) return literalRel;

// Union-to-union: each type in actual must be compatible with at least one type in expected
if (expected is TypeInfo.Union expectedUnion && actual is TypeInfo.Union actualUnion)
Expand Down
Loading