Esempio n. 1
0
        public override bool check(CodeContext context)
        {
            if (is_checked)
            {
                return(!error);
            }

            is_checked = true;

            if (class_name != null && class_name != parent_symbol.name)
            {
                // class_name is null for constructors generated by GIdlParser
                Report.error(source_reference, "missing return type in method `%s.%s´".printf(context.analyzer.current_symbol.get_full_name(), class_name));
                error = true;
                return(false);
            }

            var old_source_file = context.analyzer.current_source_file;
            var old_symbol      = context.analyzer.current_symbol;

            if (source_reference != null)
            {
                context.analyzer.current_source_file = source_reference.file;
            }
            context.analyzer.current_symbol = this;

            foreach (Parameter param in get_parameters())
            {
                param.check(context);
            }

            foreach (DataType error_type in get_error_types())
            {
                error_type.check(context);
            }

            foreach (Expression precondition in get_preconditions())
            {
                precondition.check(context);
            }

            foreach (Expression postcondition in get_postconditions())
            {
                postcondition.check(context);
            }

            if (body != null)
            {
                body.check(context);

                var cl = parent_symbol as Class;

                // ensure we chain up to base constructor
                if (!chain_up && cl != null && cl.base_class != null)
                {
                    if (cl.base_class.default_construction_method != null &&
                        !cl.base_class.default_construction_method.has_construct_function)
                    {
                        // directly chain up to Object
                        var old_insert_block = context.analyzer.insert_block;
                        context.analyzer.current_symbol = body;
                        context.analyzer.insert_block   = body;

                        var stmt = new ExpressionStatement(new MethodCall(new MemberAccess(MemberAccess.simple("GLib", source_reference), "Object", source_reference), source_reference), source_reference);
                        body.insert_statement(0, stmt);
                        stmt.check(context);

                        context.analyzer.current_symbol = this;
                        context.analyzer.insert_block   = old_insert_block;
                    }
                    else if (cl.base_class.default_construction_method == null ||
                             cl.base_class.default_construction_method.access == SymbolAccessibility.PRIVATE)
                    {
                        Report.error(source_reference, "unable to chain up to private base constructor");
                    }
                    else if (cl.base_class.default_construction_method.get_required_arguments() > 0)
                    {
                        Report.error(source_reference, "unable to chain up to base constructor requiring arguments");
                    }
                    else
                    {
                        var old_insert_block = context.analyzer.insert_block;
                        context.analyzer.current_symbol = body;
                        context.analyzer.insert_block   = body;

                        var stmt = new ExpressionStatement(new MethodCall(new BaseAccess(source_reference), source_reference), source_reference);
                        body.insert_statement(0, stmt);
                        stmt.check(context);

                        context.analyzer.current_symbol = this;
                        context.analyzer.insert_block   = old_insert_block;
                    }
                }
            }

            context.analyzer.current_source_file = old_source_file;
            context.analyzer.current_symbol      = old_symbol;

            if (is_abstract || is_virtual || overrides)
            {
                Report.error(source_reference, "The creation method `%s' cannot be marked as override, virtual, or abstract".printf(get_full_name()));
                return(false);
            }

            // check that all errors that can be thrown in the method body are declared
            if (body != null)
            {
                foreach (DataType body_error_type in body.get_error_types())
                {
                    bool can_propagate_error = false;
                    foreach (DataType method_error_type in get_error_types())
                    {
                        if (body_error_type.compatible(method_error_type))
                        {
                            can_propagate_error = true;
                        }
                    }
                    if (!can_propagate_error && !((ErrorType)body_error_type).dynamic_error)
                    {
                        Report.warning(body_error_type.source_reference, "unhandled error `%s'".printf(body_error_type.ToString()));
                    }
                }
            }

            return(!error);
        }
Esempio n. 2
0
        public override bool check(CodeContext context)
        {
            if (is_checked)
            {
                return(!error);
            }

            is_checked = true;

            if (left is ValaTuple && Operator == AssignmentOperator.SIMPLE && parent_node is ExpressionStatement)
            {
                var tuple = (ValaTuple)left;

                var local = new LocalVariable(null, get_temp_name(), right, right.source_reference);
                var decl  = new DeclarationStatement(local, source_reference);
                decl.check(context);
                insert_statement(context.analyzer.insert_block, decl);

                int i = 0;
                ExpressionStatement stmt = null;
                foreach (var expr in tuple.get_expressions())
                {
                    if (stmt != null)
                    {
                        stmt.check(context);
                        insert_statement(context.analyzer.insert_block, stmt);
                    }

                    var temp_access = MemberAccess.simple(local.name, right.source_reference);
                    var ea          = new ElementAccess(temp_access, expr.source_reference);
                    ea.append_index(new IntegerLiteral(i.ToString(), expr.source_reference));
                    var assign = new Assignment(expr, ea, Operator, expr.source_reference);
                    stmt = new ExpressionStatement(assign, expr.source_reference);

                    i++;
                }

                context.analyzer.replaced_nodes.Add(this);
                parent_node.replace_expression(this, stmt.expression);
                return(stmt.expression.check(context));
            }

            left.lvalue = true;

            if (!left.check(context))
            {
                // skip on error in inner expression
                error = true;
                return(false);
            }

            if (left is MemberAccess)
            {
                var ma = (MemberAccess)left;

                if (ma.symbol_reference is Constant)
                {
                    error = true;
                    Report.error(source_reference, "Assignment to constant after initialization");
                    return(false);
                }

                if ((!(ma.symbol_reference is Signal || ma.symbol_reference is DynamicProperty) && ma.value_type == null) ||
                    (ma.inner == null && ma.member_name == "this" && context.analyzer.is_in_instance_method()))
                {
                    error = true;
                    Report.error(source_reference, "unsupported lvalue in assignment");
                    return(false);
                }
                if (ma.prototype_access)
                {
                    error = true;
                    Report.error(source_reference, "Access to instance member `%s' denied".printf(ma.symbol_reference.get_full_name()));
                    return(false);
                }

                if (ma.error || ma.symbol_reference == null)
                {
                    error = true;
                    /* if no symbol found, skip this check */
                    return(false);
                }

                if (ma.symbol_reference is DynamicSignal)
                {
                    // target_type not available for dynamic signals
                    if (!context.deprecated)
                    {
                        Report.warning(source_reference, "deprecated syntax, use `connect' method instead");
                    }
                }
                else if (ma.symbol_reference is Signal)
                {
                    if (!context.deprecated)
                    {
                        Report.warning(source_reference, "deprecated syntax, use `connect' method instead");
                    }
                    var sig = (Signal)ma.symbol_reference;
                    right.target_type = new DelegateType(sig.get_delegate(ma.inner.value_type, this));
                }
                else if (ma.symbol_reference is DynamicProperty)
                {
                    // target_type not available for dynamic properties
                }
                else
                {
                    right.formal_target_type = ma.formal_value_type.copy();
                    right.target_type        = ma.value_type.copy();
                }
            }
            else if (left is ElementAccess)
            {
                var ea = (ElementAccess)left;

                if (ea.container.value_type.data_type == context.analyzer.string_type.data_type)
                {
                    error = true;
                    Report.error(ea.source_reference, "strings are immutable");
                    return(false);
                }
                else if (ea.container is MemberAccess && ea.container.symbol_reference is Signal)
                {
                    var ma  = (MemberAccess)ea.container;
                    var sig = (Signal)ea.container.symbol_reference;
                    right.target_type = new DelegateType(sig.get_delegate(ma.inner.value_type, this));
                }
                else if (ea.container.value_type.get_member("set") is Method)
                {
                    var set_call = new MethodCall(new MemberAccess(ea.container, "set", source_reference), source_reference);
                    foreach (Expression e in ea.get_indices())
                    {
                        set_call.add_argument(e);
                    }
                    set_call.add_argument(right);
                    parent_node.replace_expression(this, set_call);
                    return(set_call.check(context));
                }
                else
                {
                    right.target_type = left.value_type;
                }
            }
            else if (left is PointerIndirection)
            {
                right.target_type = left.value_type;
            }
            else
            {
                error = true;
                Report.error(source_reference, "unsupported lvalue in assignment");
                return(false);
            }

            if (!right.check(context))
            {
                // skip on error in inner expression
                error = true;
                return(false);
            }

            if (Operator != AssignmentOperator.SIMPLE && left is MemberAccess)
            {
                // transform into simple assignment
                // FIXME: only do this if the backend doesn't support
                // the assignment natively

                var ma = (MemberAccess)left;

                if (!(ma.symbol_reference is Signal))
                {
                    var old_value = new MemberAccess(ma.inner, ma.member_name);

                    var bin = new BinaryExpression(BinaryOperator.PLUS, old_value, right, source_reference);
                    bin.target_type               = right.target_type;
                    right.target_type             = right.target_type.copy();
                    right.target_type.value_owned = false;

                    if (Operator == AssignmentOperator.BITWISE_OR)
                    {
                        bin.Operator = BinaryOperator.BITWISE_OR;
                    }
                    else if (Operator == AssignmentOperator.BITWISE_AND)
                    {
                        bin.Operator = BinaryOperator.BITWISE_AND;
                    }
                    else if (Operator == AssignmentOperator.BITWISE_XOR)
                    {
                        bin.Operator = BinaryOperator.BITWISE_XOR;
                    }
                    else if (Operator == AssignmentOperator.ADD)
                    {
                        bin.Operator = BinaryOperator.PLUS;
                    }
                    else if (Operator == AssignmentOperator.SUB)
                    {
                        bin.Operator = BinaryOperator.MINUS;
                    }
                    else if (Operator == AssignmentOperator.MUL)
                    {
                        bin.Operator = BinaryOperator.MUL;
                    }
                    else if (Operator == AssignmentOperator.DIV)
                    {
                        bin.Operator = BinaryOperator.DIV;
                    }
                    else if (Operator == AssignmentOperator.PERCENT)
                    {
                        bin.Operator = BinaryOperator.MOD;
                    }
                    else if (Operator == AssignmentOperator.SHIFT_LEFT)
                    {
                        bin.Operator = BinaryOperator.SHIFT_LEFT;
                    }
                    else if (Operator == AssignmentOperator.SHIFT_RIGHT)
                    {
                        bin.Operator = BinaryOperator.SHIFT_RIGHT;
                    }

                    right = bin;
                    right.check(context);

                    Operator = AssignmentOperator.SIMPLE;
                }
            }

            if (left.symbol_reference is Signal)
            {
                var sig = (Signal)left.symbol_reference;

                var m = right.symbol_reference as Method;

                if (m == null)
                {
                    error = true;
                    Report.error(right.source_reference, "unsupported expression for signal handler");
                    return(false);
                }

                var dynamic_sig = sig as DynamicSignal;
                var right_ma    = right as MemberAccess;
                if (dynamic_sig != null)
                {
                    bool first = true;
                    foreach (Parameter param in dynamic_sig.handler.value_type.get_parameters())
                    {
                        if (first)
                        {
                            // skip sender parameter
                            first = false;
                        }
                        else
                        {
                            dynamic_sig.add_parameter(param.copy());
                        }
                    }
                    right.target_type = new DelegateType(sig.get_delegate(new ObjectType((ObjectTypeSymbol)sig.parent_symbol), this));
                }
                else if (!right.value_type.compatible(right.target_type))
                {
                    var delegate_type = (DelegateType)right.target_type;

                    error = true;
                    Report.error(right.source_reference, "method `%s' is incompatible with signal `%s', expected `%s'".printf(right.value_type.ToString(), right.target_type.ToString(), delegate_type.delegate_symbol.get_prototype_string(m.name)));
                    return(false);
                }
                else if (right_ma != null && right_ma.prototype_access)
                {
                    error = true;
                    Report.error(right.source_reference, "Access to instance member `%s' denied".printf(m.get_full_name()));
                    return(false);
                }
            }
            else if (left is MemberAccess)
            {
                var ma = (MemberAccess)left;

                if (ma.symbol_reference is Property)
                {
                    var prop = (Property)ma.symbol_reference;

                    var dynamic_prop = prop as DynamicProperty;
                    if (dynamic_prop != null)
                    {
                        dynamic_prop.property_type = right.value_type.copy();
                        left.value_type            = dynamic_prop.property_type.copy();
                    }

                    if (prop.set_accessor == null ||
                        (!prop.set_accessor.writable && !(context.analyzer.find_current_method() is CreationMethod || context.analyzer.is_in_constructor())))
                    {
                        ma.error = true;
                        Report.error(ma.source_reference, "Property `%s' is read-only".printf(prop.get_full_name()));
                        return(false);
                    }
                    else if (!context.deprecated &&
                             !prop.set_accessor.writable &&
                             context.analyzer.find_current_method() is CreationMethod)
                    {
                        if (ma.inner.symbol_reference != context.analyzer.find_current_method().this_parameter)
                        {
                            // trying to set construct-only property in creation method for foreign instance
                            Report.error(ma.source_reference, "Property `%s' is read-only".printf(prop.get_full_name()));
                            return(false);
                        }
                        else
                        {
                            Report.error(ma.source_reference, "Cannot assign to construct-only properties, use Object (property: value) constructor chain up");
                            return(false);
                        }
                    }
                }
                else if (ma.symbol_reference is Variable && right.value_type == null)
                {
                    var variable = (Variable)ma.symbol_reference;

                    if (right.symbol_reference is Method &&
                        variable.variable_type is DelegateType)
                    {
                        var m  = (Method)right.symbol_reference;
                        var dt = (DelegateType)variable.variable_type;
                        var cb = dt.delegate_symbol;

                        /* check whether method matches callback type */
                        if (!cb.matches_method(m, dt))
                        {
                            error = true;
                            Report.error(source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf(m.get_full_name(), cb.get_full_name()));
                            return(false);
                        }

                        right.value_type = variable.variable_type;
                    }
                    else
                    {
                        error = true;
                        Report.error(source_reference, "Assignment: Invalid assignment attempt");
                        return(false);
                    }
                }

                if (left.value_type != null && right.value_type != null)
                {
                    /* if there was an error on either side,
                     * i.e. {left|right}.value_type == null, skip type check */

                    if (!right.value_type.compatible(left.value_type))
                    {
                        error = true;
                        Report.error(source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf(right.value_type.ToString(), left.value_type.ToString()));
                        return(false);
                    }

                    if (!(ma.symbol_reference is Property))
                    {
                        if (right.value_type.is_disposable())
                        {
                            /* rhs transfers ownership of the expression */
                            if (!(left.value_type is PointerType) && !left.value_type.value_owned)
                            {
                                /* lhs doesn't own the value */
                                error = true;
                                Report.error(source_reference, "Invalid assignment from owned expression to unowned variable");
                            }
                        }
                        else if (left.value_type.value_owned)
                        {
                            /* lhs wants to own the value
                             * rhs doesn't transfer the ownership
                             * code generator needs to add reference
                             * increment calls */
                        }
                    }
                }

                var right_ma = right as MemberAccess;
                if (right_ma != null && ma.symbol_reference == right_ma.symbol_reference)
                {
                    if (ma.symbol_reference is LocalVariable || ma.symbol_reference is Parameter)
                    {
                        Report.warning(source_reference, "Assignment to same variable");
                    }
                    else if (ma.symbol_reference is Field)
                    {
                        var f = (Field)ma.symbol_reference;
                        if (f.binding == MemberBinding.STATIC)
                        {
                            Report.warning(source_reference, "Assignment to same variable");
                        }
                        else
                        {
                            var ma_inner       = ma.inner as MemberAccess;
                            var right_ma_inner = right_ma.inner as MemberAccess;
                            if (ma_inner != null && ma_inner.member_name == "this" && ma_inner.inner == null &&
                                right_ma_inner != null && right_ma_inner.member_name == "this" && right_ma_inner.inner == null)
                            {
                                Report.warning(source_reference, "Assignment to same variable");
                            }
                        }
                    }
                }
            }
            else if (left is ElementAccess)
            {
                var ea = (ElementAccess)left;

                if (!right.value_type.compatible(left.value_type))
                {
                    error = true;
                    Report.error(source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf(right.value_type.ToString(), left.value_type.ToString()));
                    return(false);
                }

                if (right.value_type.is_disposable())
                {
                    /* rhs transfers ownership of the expression */

                    DataType element_type;

                    if (ea.container.value_type is ArrayType)
                    {
                        var array_type = (ArrayType)ea.container.value_type;
                        element_type = array_type.element_type;
                    }
                    else
                    {
                        var args = ea.container.value_type.get_type_arguments();
                        Debug.Assert(args.Count == 1);
                        element_type = args[0];
                    }

                    if (!(element_type is PointerType) && !element_type.value_owned)
                    {
                        /* lhs doesn't own the value */
                        error = true;
                        Report.error(source_reference, "Invalid assignment from owned expression to unowned variable");
                        return(false);
                    }
                }
                else if (left.value_type.value_owned)
                {
                    /* lhs wants to own the value
                     * rhs doesn't transfer the ownership
                     * code generator needs to add reference
                     * increment calls */
                }
            }
            else
            {
                return(true);
            }

            if (left.value_type != null)
            {
                value_type             = left.value_type.copy();
                value_type.value_owned = false;
            }
            else
            {
                value_type = null;
            }

            add_error_types(left.get_error_types());
            add_error_types(right.get_error_types());

            return(!error);
        }
Esempio n. 3
0
        public override bool check(CodeContext context)
        {
            if (is_checked)
            {
                return(!error);
            }

            is_checked = true;

            if (!(context.analyzer.current_symbol is Block))
            {
                Report.error(source_reference, "Conditional expressions may only be used in blocks");
                error = true;
                return(false);
            }

            // convert ternary expression into if statement
            // required for flow analysis and exception handling

            string temp_name = get_temp_name();

            true_expression.target_type  = target_type;
            false_expression.target_type = target_type;

            var local = new LocalVariable(null, temp_name, null, source_reference);
            var decl  = new DeclarationStatement(local, source_reference);

            var true_local = new LocalVariable(null, temp_name, true_expression, true_expression.source_reference);
            var true_block = new Block(true_expression.source_reference);
            var true_decl  = new DeclarationStatement(true_local, true_expression.source_reference);

            true_block.add_statement(true_decl);

            var false_local = new LocalVariable(null, temp_name, false_expression, false_expression.source_reference);
            var false_block = new Block(false_expression.source_reference);
            var false_decl  = new DeclarationStatement(false_local, false_expression.source_reference);

            false_block.add_statement(false_decl);

            var if_stmt = new IfStatement(condition, true_block, false_block, source_reference);

            insert_statement(context.analyzer.insert_block, decl);
            insert_statement(context.analyzer.insert_block, if_stmt);

            if (!if_stmt.check(context) || true_expression.error || false_expression.error)
            {
                error = true;
                return(false);
            }

            true_expression  = true_local.initializer;
            false_expression = false_local.initializer;

            true_block.remove_local_variable(true_local);
            false_block.remove_local_variable(false_local);

            if (false_expression.value_type.compatible(true_expression.value_type))
            {
                value_type = true_expression.value_type.copy();
            }
            else if (true_expression.value_type.compatible(false_expression.value_type))
            {
                value_type = false_expression.value_type.copy();
            }
            else
            {
                error = true;
                Report.error(condition.source_reference, "Incompatible expressions");
                return(false);
            }

            value_type.value_owned = (true_expression.value_type.value_owned || false_expression.value_type.value_owned);

            local.variable_type = value_type;
            decl.check(context);

            true_expression.target_type  = value_type;
            false_expression.target_type = value_type;

            var true_stmt = new ExpressionStatement(new Assignment(MemberAccess.simple(local.name, true_expression.source_reference), true_expression, AssignmentOperator.SIMPLE, true_expression.source_reference), true_expression.source_reference);

            true_stmt.check(context);

            var false_stmt = new ExpressionStatement(new Assignment(MemberAccess.simple(local.name, false_expression.source_reference), false_expression, AssignmentOperator.SIMPLE, false_expression.source_reference), false_expression.source_reference);

            false_stmt.check(context);

            true_block.replace_statement(true_decl, true_stmt);
            false_block.replace_statement(false_decl, false_stmt);

            var ma = MemberAccess.simple(local.name, source_reference);

            ma.formal_target_type = formal_target_type;
            ma.target_type        = target_type;
            ma.check(context);

            parent_node.replace_expression(this, ma);

            return(true);
        }