public override void EmitCode(IBlockContext context, bool dropResult) { TO2Type leftType = left.ResultType(context); TO2Type rightType = right.ResultType(context); if (context.HasErrors) { return; } IOperatorEmitter leftEmitter = leftType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, rightType); IOperatorEmitter rightEmitter = rightType.AllowedPrefixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, leftType); if (leftEmitter == null && rightEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Cannot {op} a {leftType} with a {rightType}", Start, End )); return; } right.Prepare(context); left.EmitCode(context, false); rightEmitter?.OtherType.AssignFrom(context.ModuleContext, leftType).EmitConvert(context); right.EmitCode(context, false); leftEmitter?.OtherType.AssignFrom(context.ModuleContext, rightType).EmitConvert(context); if (context.HasErrors) { return; } if (leftEmitter != null) { leftEmitter.EmitCode(context, this); } else { rightEmitter.EmitCode(context, this); } if (dropResult) { context.IL.Emit(OpCodes.Pop); } }
public IOperatorEmitter GetMatching(ModuleContext context, Operator op, TO2Type otherType) { IOperatorEmitter existing = allowedOperators.GetMatching(context, op, otherType); if (existing != null) { return(existing); } if (op != Operator.BitAnd && op != Operator.BitAndAssign) { return(null); } RecordType otherRecordType = otherType.UnderlyingType(context) as RecordType; if (otherRecordType == null) { return(null); } bool hasMatch = false; foreach (var otherKV in otherRecordType.ItemTypes) { TO2Type item = recordType.ItemTypes.Get(otherKV.Key); if (item == null) { continue; } if (!item.IsAssignableFrom(context, otherKV.Value)) { return(null); } hasMatch = true; } return(hasMatch ? recordType.CombineFrom(otherRecordType) : null); }
public override TO2Type ResultType(IBlockContext context) { TO2Type leftType = left.ResultType(context); TO2Type rightType = right.ResultType(context); IOperatorEmitter operatorEmitter = leftType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, rightType) ?? rightType.AllowedPrefixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, leftType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Cannot {op} a {leftType} with a {rightType}", Start, End )); return(BuiltinType.Unit); } return(operatorEmitter.ResultType); }
public override void EmitCode(IBlockContext context, bool dropResult) { TO2Type valueType = expression.ResultType(context); TO2Type targetType = target.ResultType(context); IIndexAccessEmitter indexAccess = targetType.AllowedIndexAccess(context.ModuleContext, indexSpec); if (indexAccess == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoIndexAccess, $"Type '{targetType.Name}' does not support access by index", Start, End )); return; } if (target is IAssignContext assignContext) { if (assignContext.IsConst(context)) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' elements can not be set on a read-only variable", Start, End )); return; } } else { context.AddError(new StructuralError( StructuralError.ErrorType.CoreGeneration, $"Index assign '{targetType.Name}'.'{indexSpec}' on invalid target expression", Start, End )); return; } if (op == Operator.Assign) { if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(indexAccess.TargetType.UnderlyingType(context.ModuleContext)); indexAccess.EmitStore(context, subContext => { expression.EmitCode(subContext, false); indexAccess.TargetType.AssignFrom(subContext.ModuleContext, valueType) .EmitConvert(subContext); context.IL.Emit(OpCodes.Dup); // ReSharper disable once AccessToDisposedClosure tmpResult.EmitStore(subContext); }); tmpResult.EmitLoad(context); } else { indexAccess.EmitStore(context, subContext => { expression.EmitCode(subContext, false); indexAccess.TargetType.AssignFrom(subContext.ModuleContext, valueType).EmitConvert(subContext); }); } } else { IOperatorEmitter operatorEmitter = indexAccess.TargetType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, valueType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Index assign '{targetType.Name}'.'{indexSpec}': Cannot {op} a {indexAccess.TargetType} with a {valueType}", Start, End )); return; } expression.Prepare(context); if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(indexAccess.TargetType.UnderlyingType(context.ModuleContext)); indexAccess.EmitStore(context, subContext => { if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } indexAccess.EmitLoad(context); expression.EmitCode(subContext, false); operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitCode(context, this); context.IL.Emit(OpCodes.Dup); // ReSharper disable once AccessToDisposedClosure tmpResult.EmitStore(subContext); }); tmpResult.EmitLoad(context); } else { indexAccess.EmitStore(context, subContext => { if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } indexAccess.EmitLoad(context); expression.EmitCode(subContext, false); operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitCode(context, this); }); } } }
public override void EmitCode(IBlockContext context, bool dropResult) { IBlockVariable blockVariable = context.FindVariable(name); if (blockVariable == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchVariable, $"No local variable '{name}'", Start, End )); } else if (blockVariable.IsConst) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchVariable, $"Local variable '{name}' is read-only (const)", Start, End )); } if (context.HasErrors) { return; } TO2Type valueType = expression.ResultType(context); if (context.HasErrors) { return; } if (op == Operator.Assign) { EmitAssign(context, blockVariable, valueType, dropResult); return; } IOperatorEmitter operatorEmitter = blockVariable !.Type.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, valueType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Cannot {op} a {blockVariable.Type} with a {valueType}", Start, End )); return; } expression.Prepare(context); blockVariable.EmitLoad(context); expression.EmitCode(context, false); if (context.HasErrors) { return; } operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitAssign(context, blockVariable, this); if (!dropResult) { blockVariable.EmitLoad(context); } }
public override void EmitCode(IBlockContext context, bool dropResult) { TO2Type targetType = target.ResultType(context); IFieldAccessEmitter fieldAccess = targetType.FindField(context.ModuleContext, fieldName)?.Create(context.ModuleContext); TO2Type valueType = expression.ResultType(context); if (fieldAccess == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' does not have a field '{fieldName}'", Start, End )); return; } if (!fieldAccess.CanStore) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' field '{fieldName}' is read-only", Start, End )); return; } if (target is IAssignContext assignContext) { if (fieldAccess.RequiresPtr && assignContext.IsConst(context)) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' field '{fieldName}' can not be set on a read-only variable", Start, End )); return; } } else { context.AddError(new StructuralError( StructuralError.ErrorType.CoreGeneration, $"Field assign '{targetType.Name}'.'{fieldName}' on invalid target expression", Start, End )); return; } if (!fieldAccess.FieldType.IsAssignableFrom(context.ModuleContext, valueType)) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Type '{targetType.Name}' field '{fieldName}' is of type {fieldAccess.FieldType} but is assigned to {valueType}", Start, End )); return; } if (op == Operator.Assign) { if (fieldAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } expression.EmitCode(context, false); fieldAccess.FieldType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(fieldAccess.FieldType); context.IL.Emit(OpCodes.Dup); tmpResult.EmitStore(context); fieldAccess.EmitStore(context); tmpResult.EmitLoad(context); } else { fieldAccess.EmitStore(context); } } else { IOperatorEmitter operatorEmitter = fieldAccess.FieldType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, valueType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Type '{targetType.Name}' field '{fieldName}': Cannot {op} a {fieldAccess.FieldType} with a {valueType}", Start, End )); return; } expression.Prepare(context); if (fieldAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (fieldAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } fieldAccess.EmitLoad(context); expression.EmitCode(context, false); operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitCode(context, this); if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(fieldAccess.FieldType); context.IL.Emit(OpCodes.Dup); tmpResult.EmitStore(context); fieldAccess.EmitStore(context); tmpResult.EmitLoad(context); } else { fieldAccess.EmitStore(context); } } }