Skip to content

Commit

Permalink
Improve Typemaker return type inference
Browse files Browse the repository at this point in the history
  • Loading branch information
out-of-phaze committed Feb 23, 2025
1 parent a021030 commit eb945b9
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 5 deletions.
11 changes: 10 additions & 1 deletion DMCompiler/DM/DMValueType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace DMCompiler.DM;
namespace DMCompiler.DM;

// If you are modifying this, you must also modify OpenDreamShared.Dream.DreamValueType !!
// Unfortunately the client needs this and it can't reference DMCompiler due to the sandbox
Expand Down Expand Up @@ -181,6 +181,15 @@ public class DMListValueTypes(DMComplexValueType nestedListKeyType, DMComplexVal
public static DMListValueTypes MergeListValueTypes(DMCompiler compiler, DMListValueTypes type1, DMListValueTypes type2) {
return new(DMComplexValueType.MergeComplexValueTypes(compiler, type1.NestedListKeyType, type2.NestedListKeyType), (type1.NestedListValType.HasValue && type2.NestedListValType.HasValue) ? DMComplexValueType.MergeComplexValueTypes(compiler, type1.NestedListValType.Value, type2.NestedListValType.Value) : (type1.NestedListValType ?? type2.NestedListValType));
}
public static DMComplexValueType GetKeyValType(DMListValueTypes? listValueTypes) {
if (listValueTypes is null) return DMValueType.Anything;
return listValueTypes.NestedListKeyType | DMValueType.Null;
}
public static DMComplexValueType GetValueValType(DMListValueTypes? listValueTypes) {
if (listValueTypes is null) return DMValueType.Anything;
if (listValueTypes.NestedListValType is null) return listValueTypes.NestedListKeyType; // non-assoc list, keys are also values
return (DMComplexValueType)(listValueTypes.NestedListValType | DMValueType.Null);
}
public override string ToString() {
if (NestedListValType is not null)
return $"{NestedListKeyType} = {NestedListValType}";
Expand Down
8 changes: 8 additions & 0 deletions DMCompiler/DM/Expressions/Builtins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,14 @@ public override void EmitPushValue(ExpressionContext ctx) {

// initial(x)
internal class Initial(Location location, DMExpression expr) : DMExpression(location) {
public override DMComplexValueType ValType {
get {
if (Expression is LValue lValue) {
return lValue.ValType;
}
return DMValueType.Anything;
}
}
protected DMExpression Expression { get; } = expr;

public override void EmitPushValue(ExpressionContext ctx) {
Expand Down
14 changes: 11 additions & 3 deletions DMCompiler/DM/Expressions/Dereference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,27 +60,35 @@ public sealed class CallOperation : NamedOperation {
public override DreamPath? Path { get; }
public override DreamPath? NestedPath { get; }
public override bool PathIsFuzzy => Path == null;
public override DMComplexValueType ValType { get; }
public override DMComplexValueType ValType {
get {
if (_valType is null) _valType = DetermineValType(_objectTree);
return _valType.Value;
}
}
private DMComplexValueType? _valType;

private readonly DMExpression _expression;
private readonly Operation[] _operations;
private readonly DMObjectTree _objectTree;

public Dereference(DMObjectTree objectTree, Location location, DreamPath? path, DMExpression expression, Operation[] operations)
: base(location, null) {
_expression = expression;
Path = path;
_operations = operations;
_objectTree = objectTree;

if (_operations.Length == 0) {
throw new InvalidOperationException("deref expression has no operations");
}

NestedPath = _operations[^1].Path;
ValType = DetermineValType(objectTree);
}

private DMComplexValueType DetermineValType(DMObjectTree objectTree) {
var type = _expression.ValType;
if (type.IsAnything && _expression.Path is not null) type = new DMComplexValueType(DMValueType.Instance, _expression.Path);
var i = 0;
while (!type.IsAnything && i < _operations.Length) {
var operation = _operations[i++];
Expand All @@ -94,7 +102,7 @@ private DMComplexValueType DetermineValType(DMObjectTree objectTree) {

type = operation switch {
FieldOperation fieldOperation => dmObject.GetVariable(fieldOperation.Identifier)?.ValType ?? DMValueType.Anything,
IndexOperation indexOperation => indexOperation.UnnestValType(type.ListValueTypes), // TODO: Keys of assoc lists
IndexOperation indexOperation => indexOperation.UnnestValType(type.ListValueTypes),
CallOperation callOperation => dmObject.GetProcReturnTypes(callOperation.Identifier, callOperation.Parameters) ?? DMValueType.Anything,
_ => throw new InvalidOperationException("Unimplemented dereference operation")
};
Expand Down
3 changes: 2 additions & 1 deletion DMCompiler/DM/Expressions/LValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public override DMReference EmitReference(ExpressionContext ctx, string endLabel

// world
internal sealed class World(Location location) : LValue(location, DreamPath.World) {
public override DMComplexValueType ValType => new DMComplexValueType(DMValueType.Instance, DreamPath.World);
public override DMReference EmitReference(ExpressionContext ctx, string endLabel,
ShortCircuitMode shortCircuitMode = ShortCircuitMode.KeepNull) {
return DMReference.World;
Expand All @@ -93,7 +94,7 @@ public override DMComplexValueType ValType {
//todo: allow local variables to be param-typed again
// WITHOUT having to pass procParameters through the whole parser chain
//if (valType is not null) return proc.GetBaseProc().GetParameterValueTypes(valType.Value, null);
if (valType is not null) return valType.Value;
if (valType is not null && !valType.Value.IsAnything) return valType.Value;
return LocalVar.Type is not null ? new DMComplexValueType(DMValueType.Instance | DMValueType.Path | DMValueType.Null, LocalVar.Type) : DMValueType.Anything;
}
}
Expand Down

0 comments on commit eb945b9

Please sign in to comment.