private DataType InferExpressionType(ExpressionSyntax expression) { if (expression == null) { return(DataType.Unknown); } switch (expression) { case ReturnExpressionSyntax returnExpression: if (returnExpression.ReturnValue != null) { InferExpressionType(returnExpression.ReturnValue); if (returnType != null) // TODO report an error { InsertImplicitConversionIfNeeded(ref returnExpression.ReturnValue, returnType); var type = returnExpression.ReturnValue.Type; if (!IsAssignableFrom(returnType, type)) { diagnostics.Add(TypeError.CannotConvert(file, returnExpression.ReturnValue, type, returnType)); } } } else { // TODO a void or never function shouldn't have this } return(expression.Type = DataType.Never); case IntegerLiteralExpressionSyntax integerLiteral: return(expression.Type = new IntegerConstantType(integerLiteral.Value)); case StringLiteralExpressionSyntax _: return(expression.Type = DataType.StringConstant); case BoolLiteralExpressionSyntax _: return(expression.Type = DataType.Bool); case BinaryExpressionSyntax binaryOperatorExpression: return(InferBinaryExpressionType(binaryOperatorExpression)); case IdentifierNameSyntax identifierName: { var symbols = identifierName.LookupInContainingScope(); DataType type; switch (symbols.Count) { case 0: diagnostics.Add(NameBindingError.CouldNotBindName(file, identifierName.Span)); identifierName.ReferencedSymbol = UnknownSymbol.Instance; type = DataType.Unknown; break; case 1: identifierName.ReferencedSymbol = symbols.Single(); type = symbols.Single().Type; break; default: diagnostics.Add(NameBindingError.AmbiguousName(file, identifierName.Span)); identifierName.ReferencedSymbol = UnknownSymbol.Instance; type = DataType.Unknown; break; } return(identifierName.Type = type); } case UnaryExpressionSyntax unaryOperatorExpression: return(InferUnaryExpressionType(unaryOperatorExpression)); case ReferenceLifetimeSyntax lifetimeType: InferExpressionType(lifetimeType.ReferentTypeExpression); if (!IsType(lifetimeType.ReferentTypeExpression.Type)) { diagnostics.Add(TypeError.MustBeATypeExpression(file, lifetimeType.ReferentTypeExpression.Span)); } return(expression.Type = DataType.Type); case BlockSyntax blockExpression: foreach (var statement in blockExpression.Statements) { ResolveTypesInStatement(statement); } return(expression.Type = DataType.Void); // TODO assign the correct type to the block case NewObjectExpressionSyntax newObjectExpression: return(InferConstructorCallType(newObjectExpression)); case PlacementInitExpressionSyntax placementInitExpression: foreach (var argument in placementInitExpression.Arguments) { InferArgumentType(argument); } // TODO verify argument types against called function return(placementInitExpression.Type = CheckAndEvaluateTypeExpression(placementInitExpression.Initializer)); case ForeachExpressionSyntax foreachExpression: foreachExpression.Type = CheckAndEvaluateTypeExpression(foreachExpression.TypeExpression); InferExpressionType(foreachExpression.InExpression); // TODO check the break types InferExpressionType(foreachExpression.Block); // TODO assign correct type to the expression return(expression.Type = DataType.Void); case WhileExpressionSyntax whileExpression: CheckExpressionType(whileExpression.Condition, DataType.Bool); InferExpressionType(whileExpression.Block); // TODO assign correct type to the expression return(expression.Type = DataType.Void); case LoopExpressionSyntax loopExpression: InferExpressionType(loopExpression.Block); // TODO assign correct type to the expression return(expression.Type = DataType.Void); case InvocationSyntax invocation: return(InferInvocationType(invocation)); case GenericNameSyntax genericName: { foreach (var argument in genericName.Arguments) { InferExpressionType(argument.Value); } genericName.NameType.BeginFulfilling(); var nameType = InferNameType(genericName); // TODO check that argument types match function type genericName.NameType.Fulfill(nameType); switch (nameType) { case MetaFunctionType metaFunctionType: return(genericName.Type = metaFunctionType.ResultType); case UnknownType _: return(genericName.Type = DataType.Unknown); default: throw NonExhaustiveMatchException.For(genericName.NameType); } } case RefTypeSyntax refType: CheckAndEvaluateTypeExpression(refType.ReferencedType); return(refType.Type = DataType.Type); case UnsafeExpressionSyntax unsafeExpression: InferExpressionType(unsafeExpression.Expression); return(unsafeExpression.Type = unsafeExpression.Expression.Type); case MutableExpressionSyntax mutableExpression: { var expressionType = InferExpressionType(mutableExpression.Expression); DataType type; if (expressionType is Metatype) { type = DataType.Type; // It names/describes a type } else { // TODO check that the type actually is mutable so we can mutably borrow it type = expressionType; } return(mutableExpression.Type = type); } case IfExpressionSyntax ifExpression: CheckExpressionType(ifExpression.Condition, DataType.Bool); InferExpressionType(ifExpression.ThenBlock); InferExpressionType(ifExpression.ElseClause); // TODO assign a type to the expression return(ifExpression.Type = DataType.Void); case ResultExpressionSyntax resultExpression: InferExpressionType(resultExpression.Expression); return(resultExpression.Type = DataType.Never); case MemberAccessExpressionSyntax memberAccess: return(InferMemberAccessType(memberAccess)); case BreakExpressionSyntax breakExpression: InferExpressionType(breakExpression.Value); return(breakExpression.Type = DataType.Never); case AssignmentExpressionSyntax assignmentExpression: var left = InferExpressionType(assignmentExpression.LeftOperand); InferExpressionType(assignmentExpression.RightOperand); InsertImplicitConversionIfNeeded(ref assignmentExpression.RightOperand, left); var right = assignmentExpression.RightOperand.Type; if (!IsAssignableFrom(left, right)) { diagnostics.Add(TypeError.CannotConvert(file, assignmentExpression.RightOperand, right, left)); } return(assignmentExpression.Type = DataType.Void); case SelfExpressionSyntax _: return(selfType ?? DataType.Unknown); case MoveExpressionSyntax moveExpression: { var type = InferExpressionType(moveExpression.Expression); if (type is ReferenceType referenceType && !referenceType.IsOwned) { diagnostics.Add(TypeError.CannotMoveBorrowedValue(file, moveExpression)); type = referenceType.WithLifetime(Lifetime.Owned); } return(moveExpression.Type = type); } default: throw NonExhaustiveMatchException.For(expression); } }
public DataType?Evaluate(ITypeSyntax?typeSyntax) { switch (typeSyntax) { default: throw ExhaustiveMatch.Failed(typeSyntax); case null: return(null); case ITypeNameSyntax typeName: { var symbolPromises = typeName.LookupInContainingScope().ToFixedList(); switch (symbolPromises.Count) { case 0: diagnostics.Add(NameBindingError.CouldNotBindName(file, typeName.Span)); typeName.ReferencedSymbol.Fulfill(null); typeName.NamedType = DataType.Unknown; break; case 1: var symbol = symbolPromises.Single().Result; typeName.ReferencedSymbol.Fulfill(symbol); typeName.NamedType = symbol.DeclaresDataType; break; default: diagnostics.Add(NameBindingError.AmbiguousName(file, typeName.Span)); typeName.ReferencedSymbol.Fulfill(null); typeName.NamedType = DataType.Unknown; break; } break; } case ICapabilityTypeSyntax referenceCapability: { var type = Evaluate(referenceCapability.ReferentType); if (type == DataType.Unknown) { return(DataType.Unknown); } if (type is ReferenceType referenceType) { referenceCapability.NamedType = referenceType.To(referenceCapability.Capability); } else { referenceCapability.NamedType = DataType.Unknown; } break; } case IOptionalTypeSyntax optionalType: { var referent = Evaluate(optionalType.Referent); return(optionalType.NamedType = new OptionalType(referent)); } } return(typeSyntax.NamedType ?? throw new InvalidOperationException()); }