示例#1
0
        private TypeSymbol GetBinaryOperationType(TypeManagerContext context, BinaryOperationSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            var operandType1 = this.GetTypeInfoInternal(context, syntax.LeftExpression);

            CollectErrors(errors, operandType1);

            var operandType2 = this.GetTypeInfoInternal(context, syntax.RightExpression);

            CollectErrors(errors, operandType2);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // operands don't appear to have errors
            // let's match the operator now
            var operatorInfo = BinaryOperationResolver.TryMatchExact(syntax.Operator, operandType1, operandType2);

            if (operatorInfo != null)
            {
                // we found a match - use its return type
                return(operatorInfo.ReturnType);
            }

            // we do not have a match
            // operand types didn't match available operators
            return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).BinaryOperatorInvalidType(Operators.BinaryOperatorToText[syntax.Operator], operandType1.Name, operandType2.Name)));
        }
示例#2
0
        private TypeSymbol GetTernaryOperationType(TypeManagerContext context, TernaryOperationSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            // ternary operator requires the condition to be of bool type
            var conditionType = this.GetTypeInfoInternal(context, syntax.ConditionExpression);

            CollectErrors(errors, conditionType);

            var trueType = this.GetTypeInfoInternal(context, syntax.TrueExpression);

            CollectErrors(errors, trueType);

            var falseType = this.GetTypeInfoInternal(context, syntax.FalseExpression);

            CollectErrors(errors, falseType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var expectedConditionType = LanguageConstants.Bool;

            if (TypeValidator.AreTypesAssignable(conditionType, expectedConditionType) != true)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.ConditionExpression).ValueTypeMismatch(expectedConditionType.Name)));
            }

            // the return type is the union of true and false expression types
            return(UnionType.Create(trueType, falseType));
        }
示例#3
0
        private TypeSymbol GetPropertyAccessType(TypeManagerContext context, PropertyAccessSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            var baseType = this.GetTypeInfoInternal(context, syntax.BaseExpression);

            CollectErrors(errors, baseType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            if (TypeValidator.AreTypesAssignable(baseType, LanguageConstants.Object) != true)
            {
                // can only access properties of objects
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.PropertyName).ObjectRequiredForPropertyAccess(baseType)));
            }

            if (baseType.TypeKind == TypeKind.Any || !(baseType is ObjectType objectType))
            {
                return(LanguageConstants.Any);
            }

            return(this.GetNamedPropertyType(objectType, syntax.PropertyName, syntax.PropertyName.IdentifierName));
        }
示例#4
0
        private TypeSymbol GetUnaryOperationType(TypeManagerContext context, UnaryOperationSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            // TODO: When we add number type, this will have to be adjusted
            var expectedOperandType = syntax.Operator switch
            {
                UnaryOperator.Not => LanguageConstants.Bool,
                UnaryOperator.Minus => LanguageConstants.Int,
                _ => throw new NotImplementedException()
            };

            var operandType = this.GetTypeInfoInternal(context, syntax.Expression);

            CollectErrors(errors, operandType);

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            if (TypeValidator.AreTypesAssignable(operandType, expectedOperandType) != true)
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).UnaryOperatorInvalidType(Operators.UnaryOperatorToText[syntax.Operator], operandType.Name)));
            }

            return(expectedOperandType);
        }
示例#5
0
        private TypeSymbol GetParameterType(TypeManagerContext context, VariableAccessSyntax syntax, ParameterSymbol parameter)
        {
            // parameter default values can participate in cycles with their own parameters or other symbols
            // need to explicitly force a type check to detect that
            SyntaxBase?expressionToTypeCheck;

            switch (parameter.Modifier)
            {
            case ParameterDefaultValueSyntax defaultValueSyntax:
                expressionToTypeCheck = defaultValueSyntax.DefaultValue;
                break;

            case ObjectSyntax modifierSyntax:
                // technically it's redundant to type check the entire object because we have existing
                // compile-time constant checks, but it shouldn't harm anything
                expressionToTypeCheck = modifierSyntax;
                break;

            case null:
                // there's no default value or modifier - this parameter cannot participate in a cycle
                expressionToTypeCheck = null;
                break;

            default:
                throw new NotImplementedException($"Unexpected parameter modifier type '{parameter.Modifier.GetType()}");
            }

            if (expressionToTypeCheck != null && ContainsCyclicExpressionError(this.GetTypeInfoInternal(context, expressionToTypeCheck)))
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).CyclicExpression()));
            }

            return(HandleSymbolType(syntax.Name.IdentifierName, syntax.Name.Span, parameter.Type));
        }
示例#6
0
        private TypeSymbol GetVariableAccessType(TypeManagerContext context, VariableAccessSyntax syntax)
        {
            var symbol = this.ResolveSymbol(syntax);

            switch (symbol)
            {
            case ErrorSymbol errorSymbol:
                // variable bind failure - pass the error along
                return(errorSymbol.ToErrorType());

            case ResourceSymbol resource:
                return(GetResourceType(context, syntax, resource));

            case ParameterSymbol parameter:
                return(GetParameterType(context, syntax, parameter));

            case VariableSymbol variable:
                return(HandleSymbolType(syntax.Name.IdentifierName, syntax.Name.Span, variable.GetVariableType(context)));

            case OutputSymbol _:
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).OutputReferenceNotSupported(syntax.Name.IdentifierName)));

            default:
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).SymbolicNameIsNotAVariableOrParameter(syntax.Name.IdentifierName)));
            }
        }
示例#7
0
        private TypeSymbol GetArrayType(TypeManagerContext context, ArraySyntax array)
        {
            var errors = new List <ErrorDiagnostic>();

            var itemTypes = new List <TypeSymbol>(array.Children.Length);

            foreach (SyntaxBase arrayItem in array.Children)
            {
                var itemType = this.GetTypeInfoInternal(context, arrayItem);
                itemTypes.Add(itemType);
                CollectErrors(errors, itemType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            var aggregatedItemType = UnionType.Create(itemTypes);

            if (aggregatedItemType.TypeKind == TypeKind.Union || aggregatedItemType.TypeKind == TypeKind.Never)
            {
                // array contains a mix of item types or is empty
                // assume array of any for now
                return(LanguageConstants.Array);
            }

            return(new TypedArrayType(aggregatedItemType));
        }
示例#8
0
        private TypeSymbol GetStringType(TypeManagerContext context, StringSyntax @string)
        {
            if (@string.IsInterpolated() == false)
            {
                // uninterpolated strings have a known type
                return(LanguageConstants.String);
            }

            var errors = new List <ErrorDiagnostic>();

            foreach (var interpolatedExpression in @string.Expressions)
            {
                var expressionType = this.GetTypeInfoInternal(context, interpolatedExpression);
                CollectErrors(errors, expressionType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // normally we would also do an assignability check, but we allow "any" type in string interpolation expressions
            // so the assignability check cannot possibly fail (we already collected type errors from the inner expressions at this point)
            return(LanguageConstants.String);
        }
示例#9
0
        private TypeSymbol GetResourceType(TypeManagerContext context, VariableAccessSyntax syntax, ResourceSymbol resource)
        {
            // resource bodies can participate in cycles
            // need to explicitly force a type check on the body
            if (ContainsCyclicExpressionError(this.GetTypeInfoInternal(context, resource.Body)))
            {
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.Name.Span).CyclicExpression()));
            }

            return(HandleSymbolType(syntax.Name.IdentifierName, syntax.Name.Span, resource.Type));
        }
示例#10
0
        private TypeSymbol GetFunctionCallType(TypeManagerContext context, FunctionCallSyntax syntax)
        {
            var errors        = new List <ErrorDiagnostic>();
            var argumentTypes = syntax.Arguments.Select(syntax1 => GetTypeInfoInternal(context, syntax1)).ToList();

            foreach (TypeSymbol argumentType in argumentTypes)
            {
                CollectErrors(errors, argumentType);
            }

            switch (this.ResolveSymbol(syntax))
            {
            case ErrorSymbol errorSymbol:
                // function bind failure - pass the error along
                return(new ErrorTypeSymbol(errors.Concat(errorSymbol.GetDiagnostics())));

            case FunctionSymbol function:
                return(GetFunctionSymbolType(syntax, function, syntax.Arguments, argumentTypes, errors));

            default:
                return(new ErrorTypeSymbol(errors.Append(DiagnosticBuilder.ForPosition(syntax.Name.Span).SymbolicNameIsNotAFunction(syntax.Name.IdentifierName))));
            }
        }
示例#11
0
        private TypeSymbol GetObjectType(TypeManagerContext context, ObjectSyntax @object)
        {
            var errors = new List <ErrorDiagnostic>();

            foreach (ObjectPropertySyntax objectProperty in @object.Properties)
            {
                var propertyType = this.GetTypeInfoInternal(context, objectProperty);
                CollectErrors(errors, propertyType);
            }

            if (errors.Any())
            {
                return(new ErrorTypeSymbol(errors));
            }

            // type results are cached
            var properties = @object.Properties
                             .GroupBy(p => p.GetKeyText(), LanguageConstants.IdentifierComparer)
                             .Select(group => new TypeProperty(group.Key, UnionType.Create(group.Select(p => this.GetTypeInfoInternal(context, p.Value)))));

            // TODO: Add structural naming?
            return(new NamedObjectType(LanguageConstants.Object.Name, properties, additionalPropertiesType: null));
        }
示例#12
0
        private TypeSymbol GetTypeInfoInternal(TypeManagerContext context, SyntaxBase syntax)
        {
            // local function because I don't want this called directly
            TypeSymbol GetTypeInfoWithoutCache()
            {
                if (context.TryMarkVisited(syntax) == false)
                {
                    // we have already visited this node, which means we have a cycle
                    // all the nodes that were visited are involved in the cycle
                    return(new ErrorTypeSymbol(context.GetVisitedNodes().Select(node => DiagnosticBuilder.ForPosition(node.Span).CyclicExpression())));
                }

                switch (syntax)
                {
                case BooleanLiteralSyntax _:
                    return(LanguageConstants.Bool);

                case NumericLiteralSyntax _:
                    return(LanguageConstants.Int);

                case StringSyntax @string:
                    return(GetStringType(context, @string));

                case ObjectSyntax @object:
                    return(GetObjectType(context, @object));

                case ObjectPropertySyntax objectProperty:
                    return(GetTypeInfoInternal(context, objectProperty.Value));

                case ArraySyntax array:
                    return(GetArrayType(context, array));

                case ArrayItemSyntax arrayItem:
                    return(GetTypeInfoInternal(context, arrayItem.Value));

                case BinaryOperationSyntax binary:
                    return(GetBinaryOperationType(context, binary));

                case FunctionArgumentSyntax functionArgument:
                    return(GetTypeInfoInternal(context, functionArgument.Expression));

                case FunctionCallSyntax functionCall:
                    return(GetFunctionCallType(context, functionCall));

                case NullLiteralSyntax _:
                    // null is its own type
                    return(LanguageConstants.Null);

                case ParenthesizedExpressionSyntax parenthesized:
                    // parentheses don't change the type of the parenthesized expression
                    return(GetTypeInfoInternal(context, parenthesized.Expression));

                case PropertyAccessSyntax propertyAccess:
                    return(GetPropertyAccessType(context, propertyAccess));

                case ArrayAccessSyntax arrayAccess:
                    return(GetArrayAccessType(context, arrayAccess));

                case TernaryOperationSyntax ternary:
                    return(GetTernaryOperationType(context, ternary));

                case UnaryOperationSyntax unary:
                    return(GetUnaryOperationType(context, unary));

                case VariableAccessSyntax variableAccess:
                    return(GetVariableAccessType(context, variableAccess));

                case SkippedTriviaSyntax _:
                    // error should have already been raised by the ParseDiagnosticsVisitor - no need to add another
                    return(new ErrorTypeSymbol(Enumerable.Empty <ErrorDiagnostic>()));

                default:
                    return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax).InvalidExpression()));
                }
            }

            if (this.typeCheckCache.TryGetValue(syntax, out var cachedType))
            {
                // the result was already in our cache
                return(cachedType);
            }

            var type = GetTypeInfoWithoutCache();

            this.typeCheckCache.TryAdd(syntax, type);

            return(type);
        }
示例#13
0
        private TypeSymbol GetArrayAccessType(TypeManagerContext context, ArrayAccessSyntax syntax)
        {
            var errors = new List <ErrorDiagnostic>();

            var baseType = this.GetTypeInfoInternal(context, syntax.BaseExpression);

            CollectErrors(errors, baseType);

            var indexType = this.GetTypeInfoInternal(context, syntax.IndexExpression);

            CollectErrors(errors, indexType);

            if (errors.Any() || indexType.TypeKind == TypeKind.Error)
            {
                return(new ErrorTypeSymbol(errors));
            }

            if (baseType.TypeKind == TypeKind.Any)
            {
                // base expression is of type any
                if (indexType.TypeKind == TypeKind.Any)
                {
                    // index is also of type any
                    return(LanguageConstants.Any);
                }

                if (TypeValidator.AreTypesAssignable(indexType, LanguageConstants.Int) == true ||
                    TypeValidator.AreTypesAssignable(indexType, LanguageConstants.String) == true)
                {
                    // index expression is string | int but base is any
                    return(LanguageConstants.Any);
                }

                // index was of the wrong type
                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.IndexExpression).StringOrIntegerIndexerRequired(indexType)));
            }

            if (baseType is ArrayType baseArray)
            {
                // we are indexing over an array
                if (TypeValidator.AreTypesAssignable(indexType, LanguageConstants.Int) == true)
                {
                    // the index is of "any" type or integer type
                    // return the item type
                    return(baseArray.ItemType);
                }

                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.IndexExpression).ArraysRequireIntegerIndex(indexType)));
            }

            if (baseType is ObjectType baseObject)
            {
                // we are indexing over an object
                if (indexType.TypeKind == TypeKind.Any)
                {
                    // index is of type "any"
                    return(GetExpressionedPropertyType(baseObject, syntax.IndexExpression));
                }

                if (TypeValidator.AreTypesAssignable(indexType, LanguageConstants.String) == true)
                {
                    switch (syntax.IndexExpression)
                    {
                    case StringSyntax @string when @string.IsInterpolated() == false:
                        var propertyName = @string.GetLiteralValue();

                        return(this.GetNamedPropertyType(baseObject, syntax.IndexExpression, propertyName));

                    default:
                        // the property name is itself an expression
                        return(this.GetExpressionedPropertyType(baseObject, syntax.IndexExpression));
                    }
                }

                return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.IndexExpression).ObjectsRequireStringIndex(indexType)));
            }

            // index was of the wrong type
            return(new ErrorTypeSymbol(DiagnosticBuilder.ForPosition(syntax.BaseExpression).IndexerRequiresObjectOrArray(baseType)));
        }
示例#14
0
 public TypeSymbol GetTypeInfo(SyntaxBase syntax, TypeManagerContext context) => GetTypeInfoInternal(context, syntax);