Пример #1
0
        /// <summary>
        /// Evaluates a type expression to the type it identifies
        /// </summary>
        public DataType CheckAndEvaluateTypeExpression(ExpressionSyntax typeExpression)
        {
            if (typeExpression == null)
            {
                return(DataType.Unknown);
            }

            var type = InferExpressionType(typeExpression);

            if (type is UnknownType)
            {
                return(DataType.Unknown);
            }
            if (!IsType(type))
            {
                diagnostics.Add(TypeError.MustBeATypeExpression(file, typeExpression.Span));
                return(DataType.Unknown);
            }

            return(TypeExpressionEvaluator.EvaluateExpression(typeExpression));
        }
Пример #2
0
        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);
            }
        }