diff --git a/DMCompiler/DM/DMValueType.cs b/DMCompiler/DM/DMValueType.cs index a4c31598e8..a6ef541b8d 100644 --- a/DMCompiler/DM/DMValueType.cs +++ b/DMCompiler/DM/DMValueType.cs @@ -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 @@ -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}"; diff --git a/DMCompiler/DM/Expressions/Builtins.cs b/DMCompiler/DM/Expressions/Builtins.cs index 3d750b56cb..2c5e47349b 100644 --- a/DMCompiler/DM/Expressions/Builtins.cs +++ b/DMCompiler/DM/Expressions/Builtins.cs @@ -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) { diff --git a/DMCompiler/DM/Expressions/Dereference.cs b/DMCompiler/DM/Expressions/Dereference.cs index d4c088133f..74357d4107 100644 --- a/DMCompiler/DM/Expressions/Dereference.cs +++ b/DMCompiler/DM/Expressions/Dereference.cs @@ -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++]; @@ -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") }; diff --git a/DMCompiler/DM/Expressions/LValue.cs b/DMCompiler/DM/Expressions/LValue.cs index c381646dca..58cfdd5d5b 100644 --- a/DMCompiler/DM/Expressions/LValue.cs +++ b/DMCompiler/DM/Expressions/LValue.cs @@ -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; @@ -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; } }