Example #1
0
        public override VoidObject VisitVarStmt(Stmt.Var stmt)
        {
            bool sanityCheckFailed = false;

            // Sanity check the input to ensure that we don't get NullReferenceExceptions later on
            if (stmt.TypeReference == null)
            {
                TypeValidationErrorCallback(new TypeValidationError(
                                                stmt.Name,
                                                $"Internal compiler error: {stmt.Name.Lexeme} is missing a TypeReference"
                                                ));

                sanityCheckFailed = true;
            }

            if (stmt.Initializer != null && !stmt.Initializer.TypeReference.IsResolved)
            {
                TypeValidationErrorCallback(new TypeValidationError(
                                                stmt.Name,
                                                $"Internal compiler error: var {stmt.Name.Lexeme} initializer {stmt.Initializer} inference has not been attempted"
                                                ));

                sanityCheckFailed = true;
            }

            if (sanityCheckFailed)
            {
                return(VoidObject.Void);
            }

            if (stmt.TypeReference !.IsResolved)
            {
                if (stmt.Initializer != null)
                {
                    if (stmt.TypeReference.IsNullObject && stmt.Initializer.TypeReference.IsNullObject)
                    {
                        // TODO: Use stmt.Initializer.Token here instead of stmt.name, #189
                        TypeValidationErrorCallback(new TypeValidationError(
                                                        stmt.Name,
                                                        "Cannot assign null to an implicitly typed local variable"
                                                        ));
                    }
                    else if (!TypeCoercer.CanBeCoercedInto(stmt.TypeReference, stmt.Initializer.TypeReference, ((stmt.Initializer as Expr.Literal)?.Value as INumericLiteral)?.BitsUsed))
                    {
                        // TODO: Use stmt.Initializer.Token here instead of stmt.name, #189
                        TypeValidationErrorCallback(new TypeValidationError(
                                                        stmt.Name,
                                                        $"Cannot assign {stmt.Initializer.TypeReference.ClrType.ToTypeKeyword()} to {stmt.TypeReference.ClrType.ToTypeKeyword()} variable"
                                                        ));
                    }
                    else if (stmt.Initializer.TypeReference.IsNullObject)
                    {
                        // TODO: Use stmt.Initializer.Token here instead of stmt.name, #189
                        compilerWarningCallback(new CompilerWarning("Initializing variable to null detected", stmt.Name, WarningType.NULL_USAGE));
                    }
                }
            }
Example #2
0
        private void VisitCallExprForGetCallee(Expr.Call call, Expr.Get get)
        {
            string methodName = get.Name.Lexeme;

            if (get.Methods.Length == 0)
            {
                if (!get.Object.TypeReference.IsResolved)
                {
                    // This is a bit of an oddball, but... We get here when an attempt is made to call a method on some
                    // undefined type. (`Foo.do_stuff`)
                    //
                    // Now, this is a compile-time error, but the problem is that it's handled by this class itself;
                    // encountering this error will not abort the tree traversal so we must avoid breaking it.
                }
                else
                {
                    // This is even more odd, but we must ensure that we have well-defined semantics in the weird case
                    // where this would happen.
                    TypeValidationErrorCallback(new TypeValidationError(
                                                    call.Paren,
                                                    $"Internal compiler error: no methods with name '{methodName}' could be found. This is a critical " +
                                                    $"error that should have aborted the compilation before the {nameof(TypesResolvedValidator)} " +
                                                    "validation is started. "
                                                    ));
                }
            }
            else if (get.Methods.Length == 1)
            {
                MethodInfo method     = get.Methods.Single();
                var        parameters = method.GetParameters();

                // There is exactly one potential method to call in this case. We use this fact to provide better
                // error messages to the caller than when calling an overloaded method.
                if (parameters.Length != call.Arguments.Count)
                {
                    TypeValidationErrorCallback(new TypeValidationError(
                                                    call.Paren,
                                                    $"Method '{methodName}' has {parameters.Length} parameter(s) but was called with {call.Arguments.Count} argument(s)"
                                                    ));

                    return;
                }

                for (int i = 0; i < call.Arguments.Count; i++)
                {
                    ParameterInfo parameter = parameters[i];
                    Expr          argument  = call.Arguments[i];

                    if (!argument.TypeReference.IsResolved)
                    {
                        throw new PerlangInterpreterException(
                                  $"Internal compiler error: Argument '{argument}' to function {methodName} not resolved");
                    }

                    // FIXME: call.Token is a bit off here; it would be useful when constructing compiler warnings based
                    // on this if we could provide the token for the argument expression instead. However, the Expr type
                    // as used by 'argument' is a non-token-based expression so this is currently impossible.
                    // FIXME: `null` here has disadvantages as described elsewhere.
                    if (!TypeCoercer.CanBeCoercedInto(parameter.ParameterType, argument.TypeReference.ClrType, null))
                    {
                        // Very likely refers to a native method, where parameter names are not available at this point.
                        TypeValidationErrorCallback(new TypeValidationError(
                                                        argument.TypeReference.TypeSpecifier !,
                                                        $"Cannot pass {argument.TypeReference.ClrType.ToTypeKeyword()} argument as {parameter.ParameterType.ToTypeKeyword()} parameter to {methodName}()"));
                    }
                }
            }
            else
            {
                // Method is overloaded. Try to resolve the best match we can find.
                foreach (MethodInfo method in get.Methods)
                {
                    var parameters = method.GetParameters();

                    if (parameters.Length != call.Arguments.Count)
                    {
                        // The number of parameters do not match, so this method will never be a suitable candidate
                        // for our expression.
                        continue;
                    }

                    bool coercionsFailed = false;

                    for (int i = 0; i < call.Arguments.Count; i++)
                    {
                        ParameterInfo parameter = parameters[i];
                        Expr          argument  = call.Arguments[i];

                        if (!argument.TypeReference.IsResolved)
                        {
                            throw new PerlangInterpreterException(
                                      $"Internal compiler error: Argument '{argument}' to method {methodName} not resolved");
                        }

                        // FIXME: The same caveat as above with call.Token applies here as well.
                        if (!TypeCoercer.CanBeCoercedInto(parameter.ParameterType, argument.TypeReference.ClrType, null))
                        {
                            coercionsFailed = true;
                            break;
                        }
                    }

                    if (!coercionsFailed)
                    {
                        // We have found a suitable overload to use. Update the expression
                        get.Methods = ImmutableArray.Create(method);
                        return;
                    }
                }

                TypeValidationErrorCallback(new NameResolutionTypeValidationError(
                                                call.Paren,
                                                $"Method '{call.CalleeToString}' found, but no overload matches the provided parameters."
                                                ));
            }
        }
Example #3
0
        private void VisitCallExprForOtherCallee(Expr.Call expr)
        {
            Binding binding = GetVariableOrFunctionCallback(expr);

            if (binding == null)
            {
                TypeValidationErrorCallback(
                    new NameResolutionTypeValidationError(expr.Paren, $"Attempting to call undefined function '{expr.CalleeToString}'")
                    );

                return;
            }

            IList <Parameter> parameters;
            string            functionName;

            switch (binding)
            {
            case FunctionBinding functionBinding:
                Stmt.Function function = functionBinding.Function;

                if (function == null)
                {
                    throw new NameResolutionTypeValidationError(expr.Paren, $"Internal compiler error: function for {expr} not expected to be null");
                }

                parameters   = function.Parameters;
                functionName = function.Name.Lexeme;
                break;

            default:
                throw new NameResolutionTypeValidationError(expr.Paren, $"Attempting to call invalid function {binding} using {expr}");
            }

            if (parameters.Count != expr.Arguments.Count)
            {
                TypeValidationErrorCallback(new TypeValidationError(
                                                expr.Paren,
                                                $"Function '{functionName}' has {parameters.Count} parameter(s) but was called with {expr.Arguments.Count} argument(s)")
                                            );

                return;
            }

            for (int i = 0; i < expr.Arguments.Count; i++)
            {
                Parameter parameter = parameters[i];
                Expr      argument  = expr.Arguments[i];

                if (!argument.TypeReference.IsResolved)
                {
                    throw new PerlangInterpreterException($"Internal compiler error: Argument '{argument}' to function {functionName} not resolved");
                }

                if (argument.TypeReference.IsNullObject)
                {
                    compilerWarningCallback(new CompilerWarning($"Null parameter detected for '{parameter.Name.Lexeme}'", expr.TokenAwareCallee.Token, WarningType.NULL_USAGE));
                }

                // FIXME: expr.Token is an approximation here as well (see other similar comments in this file)
                // FIXME: `null` here means that small-constants of e.g. `long` will not be able to be passed as `int` parameters.
                if (!TypeCoercer.CanBeCoercedInto(parameter.TypeReference, argument.TypeReference, null))
                {
                    if (parameter.Name != null)
                    {
                        TypeValidationErrorCallback(new TypeValidationError(
                                                        argument.TypeReference.TypeSpecifier !,
                                                        $"Cannot pass {argument.TypeReference.ClrType.ToTypeKeyword()} argument as parameter '{parameter.Name.Lexeme}: {parameter.TypeReference.ClrType.ToTypeKeyword()}' to {functionName}()"));
                    }
                    else
                    {
                        // Very likely refers to a native method, where parameter names are not available at this point.
                        TypeValidationErrorCallback(new TypeValidationError(
                                                        argument.TypeReference.TypeSpecifier !,
                                                        $"Cannot pass {argument.TypeReference.ClrType.ToTypeKeyword()} argument as {parameter.TypeReference.ClrType.ToTypeKeyword()} parameter to {functionName}()"));
                    }
                }
            }
        }