Example #1
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);
        }
Example #2
0
        public override bool check(CodeContext context)
        {
            if (is_checked)
            {
                return(!error);
            }

            is_checked = true;

            // some expressions are not in a block,
            // for example, expressions in method contracts
            if (context.analyzer.current_symbol is Block &&
                (Operator == BinaryOperator.AND || Operator == BinaryOperator.OR))
            {
                // convert conditional expression into if statement
                // required for flow analysis and exception handling

                var local = new LocalVariable(context.analyzer.bool_type.copy(), get_temp_name(), null, source_reference);
                var decl  = new DeclarationStatement(local, source_reference);
                decl.check(context);

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

                var stmt = new ExpressionStatement(
                    new Assignment(
                        MemberAccess.simple(local.name, left.source_reference),
                        new BooleanLiteral((Operator == BinaryOperator.OR), left.source_reference),
                        AssignmentOperator.SIMPLE, left.source_reference
                        ), left.source_reference
                    );

                var true_block  = new Block(source_reference);
                var false_block = new Block(source_reference);

                if (Operator == BinaryOperator.AND)
                {
                    true_block.add_statement(right_stmt);
                    false_block.add_statement(stmt);
                }
                else
                {
                    true_block.add_statement(stmt);
                    false_block.add_statement(right_stmt);
                }

                var if_stmt = new IfStatement(left, 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))
                {
                    error = true;
                    return(false);
                }

                var ma = MemberAccess.simple(local.name, source_reference);
                ma.target_type        = target_type;
                ma.formal_target_type = formal_target_type;
                ma.check(context);

                parent_node.replace_expression(this, ma);

                return(true);
            }

            if (Operator == BinaryOperator.COALESCE)
            {
                if (!left.check(context))
                {
                    error = true;
                    return(false);
                }

                if (!right.check(context))
                {
                    error = true;
                    return(false);
                }

                DataType local_type    = null;
                bool     cast_non_null = false;
                if (left.value_type is NullType && right.value_type != null)
                {
                    Report.warning(left.source_reference, "left operand is always null");
                    local_type          = right.value_type.copy();
                    local_type.nullable = true;
                    if (!right.value_type.nullable)
                    {
                        cast_non_null = true;
                    }
                }
                else if (left.value_type != null)
                {
                    local_type = left.value_type.copy();
                    if (right.value_type != null && right.value_type.value_owned)
                    {
                        // value owned if either left or right is owned
                        local_type.value_owned = true;
                    }
                    if (context.experimental_non_null)
                    {
                        if (!local_type.nullable)
                        {
                            Report.warning(left.source_reference, "left operand is never null");
                            if (right.value_type != null && right.value_type.nullable)
                            {
                                local_type.nullable = true;
                                cast_non_null       = true;
                            }
                        }
                        else if (right.value_type != null && !right.value_type.nullable)
                        {
                            cast_non_null = true;
                        }
                    }
                }
                else if (right.value_type != null)
                {
                    local_type = right.value_type.copy();
                }

                var local = new LocalVariable(local_type, get_temp_name(), left, source_reference);
                var decl  = new DeclarationStatement(local, source_reference);

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

                var true_block = new Block(source_reference);

                true_block.add_statement(right_stmt);

                var cond = new BinaryExpression(BinaryOperator.EQUALITY, MemberAccess.simple(local.name, left.source_reference), new NullLiteral(source_reference), source_reference);

                var if_stmt = new IfStatement(cond, true_block, null, source_reference);

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

                if (!decl.check(context))
                {
                    error = true;
                    return(false);
                }

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

                var replace_expr = SemanticAnalyzer.create_temp_access(local, target_type);
                if (cast_non_null && replace_expr.target_type != null)
                {
                    var cast = CastExpression.non_null(replace_expr, source_reference);
                    cast.target_type          = replace_expr.target_type.copy();
                    cast.target_type.nullable = false;
                    replace_expr = cast;
                }
                replace_expr.check(context);

                parent_node.replace_expression(this, replace_expr);

                return(true);
            }

            if (!left.check(context) || !right.check(context))
            {
                /* if there were any errors in inner expressions, skip type check */
                error = true;
                return(false);
            }

            if (left.value_type == null)
            {
                Report.error(left.source_reference, "invalid left operand");
                error = true;
                return(false);
            }

            if (Operator != BinaryOperator.IN && right.value_type == null)
            {
                Report.error(right.source_reference, "invalid right operand");
                error = true;
                return(false);
            }

            if (left.value_type is FieldPrototype)
            {
                error = true;
                Report.error(left.source_reference, "Access to instance member `%s' denied".printf(left.symbol_reference.get_full_name()));
                return(false);
            }
            if (right.value_type is FieldPrototype)
            {
                error = true;
                Report.error(right.source_reference, "Access to instance member `%s' denied".printf(right.symbol_reference.get_full_name()));
                return(false);
            }

            left.target_type              = left.value_type.copy();
            left.target_type.value_owned  = false;
            right.target_type             = right.value_type.copy();
            right.target_type.value_owned = false;

            if (left.value_type.data_type == context.analyzer.string_type.data_type &&
                Operator == BinaryOperator.PLUS)
            {
                // string concatenation

                if (right.value_type == null || right.value_type.data_type != context.analyzer.string_type.data_type)
                {
                    error = true;
                    Report.error(source_reference, "Operands must be strings");
                    return(false);
                }

                value_type = context.analyzer.string_type.copy();
                if (left.is_constant() && right.is_constant())
                {
                    value_type.value_owned = false;
                }
                else
                {
                    value_type.value_owned = true;
                }
            }
            else if (left.value_type is ArrayType && Operator == BinaryOperator.PLUS)
            {
                // array concatenation

                var array_type = (ArrayType)left.value_type;

                if (right.value_type == null || !right.value_type.compatible(array_type.element_type))
                {
                    error = true;
                    Report.error(source_reference, "Incompatible operand");
                    return(false);
                }

                right.target_type = array_type.element_type.copy();

                value_type             = array_type.copy();
                value_type.value_owned = true;
            }
            else if (
                Operator == BinaryOperator.PLUS ||
                Operator == BinaryOperator.MINUS ||
                Operator == BinaryOperator.MUL ||
                Operator == BinaryOperator.DIV
                )
            {
                // check for pointer arithmetic
                if (left.value_type is PointerType)
                {
                    var pointer_type = (PointerType)left.value_type;
                    if (pointer_type.base_type is VoidType)
                    {
                        error = true;
                        Report.error(source_reference, "Pointer arithmetic not supported for `void*'");
                        return(false);
                    }

                    var offset_type = right.value_type.data_type as Struct;
                    if (offset_type != null && offset_type.is_integer_type())
                    {
                        if (Operator == BinaryOperator.PLUS ||
                            Operator == BinaryOperator.MINUS)
                        {
                            // pointer arithmetic: pointer +/- offset
                            value_type = left.value_type.copy();
                        }
                    }
                    else if (right.value_type is PointerType)
                    {
                        // pointer arithmetic: pointer - pointer
                        value_type = context.analyzer.size_t_type;
                    }
                }
                else
                {
                    left.target_type.nullable  = false;
                    right.target_type.nullable = false;
                }

                if (value_type == null)
                {
                    value_type = context.analyzer.get_arithmetic_result_type(left.target_type, right.target_type);
                }

                if (value_type == null)
                {
                    error = true;
                    Report.error(source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf(left.value_type.ToString(), right.value_type.ToString()));
                    return(false);
                }
            }
            else if (
                Operator == BinaryOperator.MOD ||
                Operator == BinaryOperator.SHIFT_LEFT ||
                Operator == BinaryOperator.SHIFT_RIGHT
                )
            {
                left.target_type.nullable  = false;
                right.target_type.nullable = false;

                value_type = context.analyzer.get_arithmetic_result_type(left.target_type, right.target_type);

                if (value_type == null)
                {
                    error = true;
                    Report.error(source_reference, "Arithmetic operation not supported for types `%s' and `%s'".printf(left.value_type.ToString(), right.value_type.ToString()));
                    return(false);
                }
            }
            else if (Operator == BinaryOperator.LESS_THAN ||
                     Operator == BinaryOperator.GREATER_THAN ||
                     Operator == BinaryOperator.LESS_THAN_OR_EQUAL ||
                     Operator == BinaryOperator.GREATER_THAN_OR_EQUAL
                     )
            {
                if (left.value_type.compatible(context.analyzer.string_type) &&
                    right.value_type.compatible(context.analyzer.string_type))
                {
                    // string comparison
                }
                else if (left.value_type is PointerType && right.value_type is PointerType)
                {
                    // pointer arithmetic
                }
                else
                {
                    DataType resulting_type;

                    if (chained)
                    {
                        var lbe = (BinaryExpression)left;
                        resulting_type = context.analyzer.get_arithmetic_result_type(lbe.right.target_type, right.target_type);
                    }
                    else
                    {
                        resulting_type = context.analyzer.get_arithmetic_result_type(left.target_type, right.target_type);
                    }

                    if (resulting_type == null)
                    {
                        error = true;
                        Report.error(source_reference, "Relational operation not supported for types `%s' and `%s'".printf(left.value_type.ToString(), right.value_type.ToString()));
                        return(false);
                    }

                    if (!chained)
                    {
                        left.target_type = resulting_type.copy();
                    }
                    right.target_type          = resulting_type.copy();
                    left.target_type.nullable  = false;
                    right.target_type.nullable = false;
                }

                value_type = context.analyzer.bool_type;
            }
            else if (
                Operator == BinaryOperator.EQUALITY ||
                Operator == BinaryOperator.INEQUALITY
                )
            {
                /* relational operation */

                if (!right.value_type.compatible(left.value_type) &&
                    !left.value_type.compatible(right.value_type))
                {
                    Report.error(source_reference, "Equality operation: `%s' and `%s' are incompatible".printf(right.value_type.ToString(), left.value_type.ToString()));
                    error = true;
                    return(false);
                }

                var resulting_type = context.analyzer.get_arithmetic_result_type(left.target_type, right.target_type);
                if (resulting_type != null)
                {
                    // numeric operation
                    left.target_type  = resulting_type.copy();
                    right.target_type = resulting_type.copy();
                }

                left.target_type.value_owned  = false;
                right.target_type.value_owned = false;

                if (left.value_type.nullable != right.value_type.nullable)
                {
                    // if only one operand is nullable, make sure the other
                    // operand is promoted to nullable as well,
                    // reassign both, as get_arithmetic_result_type doesn't
                    // take nullability into account
                    left.target_type.nullable  = true;
                    right.target_type.nullable = true;
                }

                value_type = context.analyzer.bool_type;
            }
            else if (
                Operator == BinaryOperator.BITWISE_AND ||
                Operator == BinaryOperator.BITWISE_OR ||
                Operator == BinaryOperator.BITWISE_XOR
                )
            {
                // integer type or flags type
                left.target_type.nullable  = false;
                right.target_type.nullable = false;

                value_type = left.target_type.copy();
            }
            else if (
                Operator == BinaryOperator.AND ||
                Operator == BinaryOperator.OR
                )
            {
                if (!left.value_type.compatible(context.analyzer.bool_type) || !right.value_type.compatible(context.analyzer.bool_type))
                {
                    error = true;
                    Report.error(source_reference, "Operands must be boolean");
                }
                left.target_type.nullable  = false;
                right.target_type.nullable = false;

                value_type = context.analyzer.bool_type;
            }
            else if (Operator == BinaryOperator.IN)
            {
                if (left.value_type.compatible(context.analyzer.int_type) &&
                    right.value_type.compatible(context.analyzer.int_type))
                {
                    // integers or enums
                    left.target_type.nullable  = false;
                    right.target_type.nullable = false;
                }
                else if (right.value_type is ArrayType)
                {
                    if (!left.value_type.compatible(((ArrayType)right.value_type).element_type))
                    {
                        Report.error(source_reference, "Cannot look for `%s' in `%s'".printf(left.value_type.ToString(), right.value_type.ToString()));
                    }
                }
                else
                {
                    // otherwise require a bool contains () method
                    var contains_method = right.value_type.get_member("contains") as Method;
                    if (contains_method == null)
                    {
                        Report.error(source_reference, "`%s' does not have a `contains' method".printf(right.value_type.ToString()));
                        error = true;
                        return(false);
                    }
                    if (contains_method.get_parameters().Count != 1)
                    {
                        Report.error(source_reference, "`%s' must have one parameter".printf(contains_method.get_full_name()));
                        error = true;
                        return(false);
                    }
                    if (!contains_method.return_type.compatible(context.analyzer.bool_type))
                    {
                        Report.error(source_reference, "`%s' must return a boolean value".printf(contains_method.get_full_name()));
                        error = true;
                        return(false);
                    }

                    var contains_call = new MethodCall(new MemberAccess(right, "contains", source_reference), source_reference);
                    contains_call.add_argument(left);
                    parent_node.replace_expression(this, contains_call);
                    return(contains_call.check(context));
                }

                value_type = context.analyzer.bool_type;
            }
            else
            {
                assert_not_reached();
            }

            return(!error);
        }
Example #3
0
        public override bool check(CodeContext context)
        {
            if (is_checked)
            {
                return(!error);
            }

            is_checked = true;

            if (inner != null)
            {
                inner.check(context);
            }

            foreach (DataType type_arg in type_argument_list)
            {
                type_arg.check(context);
            }

            Symbol    base_symbol    = null;
            Parameter this_parameter = null;
            bool      may_access_instance_members = false;
            bool      may_access_klass_members    = false;

            symbol_reference = null;

            if (qualified)
            {
                base_symbol      = context.analyzer.root_symbol;
                symbol_reference = context.analyzer.root_symbol.scope.lookup(member_name);
            }
            else if (inner == null)
            {
                if (member_name == "this")
                {
                    if (!context.analyzer.is_in_instance_method())
                    {
                        error = true;
                        Report.error(source_reference, "This access invalid outside of instance methods");
                        return(false);
                    }
                }

                base_symbol = context.analyzer.current_symbol;

                // track whether method has been found to make sure that access
                // to instance member is denied from within static lambda expressions
                bool method_found = false;

                var sym = context.analyzer.current_symbol;
                while (sym != null && symbol_reference == null)
                {
                    if (!method_found)
                    {
                        if (sym is CreationMethod)
                        {
                            var cm = (CreationMethod)sym;
                            this_parameter = cm.this_parameter;
                            may_access_instance_members = true;
                            may_access_klass_members    = true;
                            method_found = true;
                        }
                        else if (sym is Property)
                        {
                            var prop = (Property)sym;
                            this_parameter = prop.this_parameter;
                            may_access_instance_members = (prop.binding == MemberBinding.INSTANCE);
                            may_access_klass_members    = (prop.binding != MemberBinding.STATIC);
                            method_found = true;
                        }
                        else if (sym is Constructor)
                        {
                            var c = (Constructor)sym;
                            this_parameter = c.this_parameter;
                            may_access_instance_members = (c.binding == MemberBinding.INSTANCE);
                            may_access_klass_members    = true;
                            method_found = true;
                        }
                        else if (sym is Destructor)
                        {
                            var d = (Destructor)sym;
                            this_parameter = d.this_parameter;
                            may_access_instance_members = (d.binding == MemberBinding.INSTANCE);
                            may_access_klass_members    = true;
                            method_found = true;
                        }
                        else if (sym is Method)
                        {
                            var m = (Method)sym;
                            this_parameter = m.this_parameter;
                            may_access_instance_members = (m.binding == MemberBinding.INSTANCE);
                            may_access_klass_members    = (m.binding != MemberBinding.STATIC);
                            method_found = true;
                        }
                    }

                    symbol_reference = SemanticAnalyzer.symbol_lookup_inherited(sym, member_name);

                    if (symbol_reference == null && sym is TypeSymbol && may_access_instance_members)
                    {
                        // used for generated to_string methods in enums
                        symbol_reference = this_parameter.variable_type.get_member(member_name);

                        if (symbol_reference != null && is_instance_symbol(symbol_reference))
                        {
                            // implicit this
                            inner                        = new MemberAccess(null, "this", source_reference);
                            inner.value_type             = this_parameter.variable_type.copy();
                            inner.value_type.value_owned = false;
                            inner.symbol_reference       = this_parameter;

                            symbol_reference = inner.value_type.get_member(member_name);
                        }
                    }

                    if (symbol_reference == null)
                    {
                        if (sym is TypeSymbol)
                        {
                            // do not allow instance access to outer classes
                            this_parameter = null;
                            may_access_instance_members = false;
                            may_access_klass_members    = false;
                        }
                    }

                    sym = sym.parent_symbol;
                }

                if (symbol_reference == null && source_reference != null)
                {
                    foreach (UsingDirective ns in source_reference.using_directives)
                    {
                        var local_sym = ns.namespace_symbol.scope.lookup(member_name);
                        if (local_sym != null)
                        {
                            if (symbol_reference != null && symbol_reference != local_sym)
                            {
                                error = true;
                                Report.error(source_reference, "`%s' is an ambiguous reference between `%s' and `%s'".printf(member_name, symbol_reference.get_full_name(), local_sym.get_full_name()));
                                return(false);
                            }
                            symbol_reference = local_sym;
                        }
                    }
                }
            }
            else
            {
                if (inner.error)
                {
                    /* if there was an error in the inner expression, skip this check */
                    error = true;
                    return(false);
                }

                if (inner.value_type is PointerType)
                {
                    var pointer_type = inner.value_type as PointerType;
                    if (pointer_type != null && pointer_type.base_type is ValaValueType)
                    {
                        // transform foo->bar to (*foo).bar
                        inner = new PointerIndirection(inner, source_reference);
                        inner.check(context);
                        pointer_member_access = false;
                    }
                }

                if (inner is MemberAccess)
                {
                    var ma = (MemberAccess)inner;
                    if (ma.prototype_access)
                    {
                        error = true;
                        Report.error(source_reference, "Access to instance member `%s' denied".printf(inner.symbol_reference.get_full_name()));
                        return(false);
                    }
                }

                if (inner is MemberAccess || inner is BaseAccess)
                {
                    base_symbol = inner.symbol_reference;

                    if (symbol_reference == null && (base_symbol is Namespace || base_symbol is TypeSymbol))
                    {
                        symbol_reference = base_symbol.scope.lookup(member_name);
                        if (inner is BaseAccess)
                        {
                            // inner expression is base access
                            // access to instance members of the base type possible
                            may_access_instance_members = true;
                            may_access_klass_members    = true;
                        }
                    }
                }

                if (symbol_reference == null && inner.value_type != null)
                {
                    if (pointer_member_access)
                    {
                        symbol_reference = inner.value_type.get_pointer_member(member_name);
                    }
                    else
                    {
                        if (inner.value_type.data_type != null)
                        {
                            base_symbol = inner.value_type.data_type;
                        }
                        symbol_reference = inner.value_type.get_member(member_name);
                    }
                    if (symbol_reference != null)
                    {
                        // inner expression is variable, field, or parameter
                        // access to instance members of the corresponding type possible
                        may_access_instance_members = true;
                        may_access_klass_members    = true;
                    }
                }

                if (symbol_reference == null && inner.value_type != null && inner.value_type.is_dynamic)
                {
                    // allow late bound members for dynamic types
                    var dynamic_object_type = (ObjectType)inner.value_type;
                    if (parent_node is MethodCall)
                    {
                        var invoc = (MethodCall)parent_node;
                        if (invoc.call == this)
                        {
                            // dynamic method
                            DataType ret_type;
                            if (invoc.target_type != null)
                            {
                                ret_type             = invoc.target_type.copy();
                                ret_type.value_owned = true;
                            }
                            else if (invoc.parent_node is ExpressionStatement)
                            {
                                ret_type = new VoidType();
                            }
                            else
                            {
                                // expect dynamic object of the same type
                                ret_type = inner.value_type.copy();
                            }
                            var m = new DynamicMethod(inner.value_type, member_name, ret_type, source_reference);
                            m.invocation = invoc;
                            var err = new ErrorType(null, null);
                            err.dynamic_error = true;
                            m.add_error_type(err);
                            m.access = SymbolAccessibility.PUBLIC;
                            m.add_parameter(Parameter.with_ellipsis());
                            dynamic_object_type.type_symbol.scope.add(null, m);
                            symbol_reference = m;
                        }
                    }
                    else if (parent_node is Assignment)
                    {
                        var a = (Assignment)parent_node;
                        if (a.left == this &&
                            (a.Operator == AssignmentOperator.ADD ||
                             a.Operator == AssignmentOperator.SUB))
                        {
                            // dynamic signal
                            var s = new DynamicSignal(inner.value_type, member_name, new VoidType(), source_reference);
                            s.handler = a.right;
                            s.access  = SymbolAccessibility.PUBLIC;
                            dynamic_object_type.type_symbol.scope.add(null, s);
                            symbol_reference = s;
                        }
                        else if (a.left == this)
                        {
                            // dynamic property assignment
                            var prop = new DynamicProperty(inner.value_type, member_name, source_reference);
                            prop.access       = SymbolAccessibility.PUBLIC;
                            prop.set_accessor = new PropertyAccessor(false, true, false, null, null, prop.source_reference);
                            prop.owner        = inner.value_type.data_type.scope;
                            dynamic_object_type.type_symbol.scope.add(null, prop);
                            symbol_reference = prop;
                        }
                    }
                    else if (parent_node is MemberAccess && inner is MemberAccess && parent_node.parent_node is MethodCall)
                    {
                        var ma = (MemberAccess)parent_node;
                        if (ma.member_name == "connect" || ma.member_name == "connect_after")
                        {
                            // dynamic signal
                            var s     = new DynamicSignal(inner.value_type, member_name, new VoidType(), source_reference);
                            var mcall = (MethodCall)parent_node.parent_node;
                            // the first argument is the handler
                            if (mcall.get_argument_list().Count > 0)
                            {
                                s.handler = mcall.get_argument_list()[0];
                            }
                            s.access = SymbolAccessibility.PUBLIC;
                            dynamic_object_type.type_symbol.scope.add(null, s);
                            symbol_reference = s;
                        }
                    }
                    if (symbol_reference == null)
                    {
                        // dynamic property read access
                        var prop = new DynamicProperty(inner.value_type, member_name, source_reference);
                        if (target_type != null)
                        {
                            prop.property_type = target_type;
                        }
                        else
                        {
                            // expect dynamic object of the same type
                            prop.property_type = inner.value_type.copy();
                        }
                        prop.access       = SymbolAccessibility.PUBLIC;
                        prop.get_accessor = new PropertyAccessor(true, false, false, prop.property_type.copy(), null, prop.source_reference);
                        prop.owner        = inner.value_type.data_type.scope;
                        dynamic_object_type.type_symbol.scope.add(null, prop);
                        symbol_reference = prop;
                    }
                    if (symbol_reference != null)
                    {
                        may_access_instance_members = true;
                        may_access_klass_members    = true;
                    }
                }
            }

            if (symbol_reference == null)
            {
                error = true;

                string base_type_name = "(null)";
                if (inner != null && inner.value_type != null)
                {
                    base_type_name = inner.value_type.ToString();
                }
                else if (base_symbol != null)
                {
                    base_type_name = base_symbol.get_full_name();
                }

                Report.error(source_reference, "The name `%s' does not exist in the context of `%s'".printf(member_name, base_type_name));
                return(false);
            }

            var  member   = symbol_reference;
            var  access   = SymbolAccessibility.PUBLIC;
            bool instance = false;
            bool klass    = false;
            bool generics = false;

            if (!member.check(context))
            {
                return(false);
            }

            if (member is LocalVariable)
            {
                var local = (LocalVariable)member;
                var block = local.parent_symbol as Block;
                if (block != null && context.analyzer.find_parent_method_or_property_accessor(block) != context.analyzer.current_method_or_property_accessor)
                {
                    // mark all methods between current method and the captured
                    // block as closures (to support nested closures)
                    Symbol sym = context.analyzer.current_method_or_property_accessor;
                    while (sym != block)
                    {
                        var method = sym as Method;
                        if (method != null)
                        {
                            method.closure = true;
                            // consider captured variables as used
                            // as we require captured variables to be initialized
                            method.add_captured_variable(local);
                        }
                        sym = sym.parent_symbol;
                    }

                    local.captured = true;
                    block.captured = true;
                }
            }
            else if (member is Parameter)
            {
                var param = (Parameter)member;
                var m     = param.parent_symbol as Method;
                if (m != null && m != context.analyzer.current_method_or_property_accessor && param != m.this_parameter)
                {
                    // mark all methods between current method and the captured
                    // parameter as closures (to support nested closures)
                    Symbol sym = context.analyzer.current_method_or_property_accessor;
                    while (sym != m)
                    {
                        var method = sym as Method;
                        if (method != null)
                        {
                            method.closure = true;
                        }
                        sym = sym.parent_symbol;
                    }

                    param.captured  = true;
                    m.body.captured = true;

                    if (param.direction != ParameterDirection.IN)
                    {
                        error = true;
                        Report.error(source_reference, "Cannot capture reference or output parameter `%s'".printf(param.get_full_name()));
                    }
                }
                else
                {
                    var acc = param.parent_symbol.parent_symbol as PropertyAccessor;
                    if (acc != null && acc != context.analyzer.current_method_or_property_accessor && param != acc.prop.this_parameter)
                    {
                        // mark all methods between current method and the captured
                        // parameter as closures (to support nested closures)
                        Symbol sym = context.analyzer.current_method_or_property_accessor;
                        while (sym != m)
                        {
                            var method = sym as Method;
                            if (method != null)
                            {
                                method.closure = true;
                            }
                            sym = sym.parent_symbol;
                        }

                        param.captured    = true;
                        acc.body.captured = true;
                    }
                }
            }
            else if (member is Field)
            {
                var f = (Field)member;
                access   = f.access;
                instance = (f.binding == MemberBinding.INSTANCE);
                klass    = (f.binding == MemberBinding.CLASS);

                // do not allow access to fields of generic types
                // if instance type does not specify type arguments
                if (f.variable_type is GenericType)
                {
                    generics = true;
                }
            }
            else if (member is Constant)
            {
                var c = (Constant)member;
                access = c.access;

                var block = c.parent_symbol as Block;
                if (block != null && context.analyzer.find_parent_method_or_property_accessor(block) != context.analyzer.current_method_or_property_accessor)
                {
                    error = true;
                    Report.error(source_reference, "internal error: accessing local constants of outer methods is not supported yet");
                    return(false);
                }
            }
            else if (member is Method)
            {
                var m = (Method)member;
                if (m.is_async_callback)
                {
                    // ensure to use right callback method for virtual/abstract async methods
                    // and also for lambda expressions within async methods
                    var async_method = context.analyzer.current_async_method;

                    bool is_valid_access = false;
                    if (async_method != null)
                    {
                        if (m == async_method.get_callback_method())
                        {
                            is_valid_access = true;
                        }
                        else if (async_method.base_method != null && m == async_method.base_method.get_callback_method())
                        {
                            is_valid_access = true;
                        }
                        else if (async_method.base_interface_method != null && m == async_method.base_interface_method.get_callback_method())
                        {
                            is_valid_access = true;
                        }
                    }
                    if (!is_valid_access)
                    {
                        error = true;
                        Report.error(source_reference, "Access to async callback `%s' not allowed in this context".printf(m.get_full_name()));
                        return(false);
                    }

                    if (async_method != context.analyzer.current_method)
                    {
                        Symbol sym = context.analyzer.current_method;
                        while (sym != async_method)
                        {
                            var method = sym as Method;
                            if (method != null)
                            {
                                method.closure = true;
                            }
                            sym = sym.parent_symbol;
                        }
                        async_method.body.captured = true;
                    }

                    m = async_method.get_callback_method();
                    symbol_reference = m;
                    member           = symbol_reference;
                }
                else if (m.base_method != null)
                {
                    // refer to base method to inherit default arguments
                    m = m.base_method;

                    if (m.signal_reference != null)
                    {
                        // method is class/default handler for a signal
                        // let signal deal with member access
                        symbol_reference = m.signal_reference;
                    }
                    else
                    {
                        symbol_reference = m;
                    }

                    member = symbol_reference;
                }
                else if (m.base_interface_method != null)
                {
                    // refer to base method to inherit default arguments
                    m = m.base_interface_method;

                    if (m.signal_reference != null)
                    {
                        // method is class/default handler for a signal
                        // let signal deal with member access
                        symbol_reference = m.signal_reference;
                    }
                    else
                    {
                        symbol_reference = m;
                    }

                    member = symbol_reference;
                }
                access = m.access;
                if (!(m is CreationMethod))
                {
                    instance = (m.binding == MemberBinding.INSTANCE);
                }
                klass = (m.binding == MemberBinding.CLASS);

                // do not allow access to methods using generic type parameters
                // if instance type does not specify type arguments
                foreach (var param in m.get_parameters())
                {
                    var _generic_type = param.variable_type as GenericType;
                    if (_generic_type != null && _generic_type.type_parameter.parent_symbol is TypeSymbol)
                    {
                        generics = true;
                        break;
                    }
                }
                var generic_type = m.return_type as GenericType;
                if (generic_type != null && generic_type.type_parameter.parent_symbol is TypeSymbol)
                {
                    generics = true;
                }
            }
            else if (member is Property)
            {
                var prop = (Property)member;
                if (!prop.check(context))
                {
                    error = true;
                    return(false);
                }
                if (prop.base_property != null)
                {
                    // refer to base property
                    prop             = prop.base_property;
                    symbol_reference = prop;
                    member           = symbol_reference;
                }
                else if (prop.base_interface_property != null)
                {
                    // refer to base property
                    prop             = prop.base_interface_property;
                    symbol_reference = prop;
                    member           = symbol_reference;
                }
                access = prop.access;
                if (lvalue)
                {
                    if (prop.set_accessor == null)
                    {
                        error = true;
                        Report.error(source_reference, "Property `%s' is read-only".printf(prop.get_full_name()));
                        return(false);
                    }
                    if (prop.access == SymbolAccessibility.PUBLIC)
                    {
                        access = prop.set_accessor.access;
                    }
                    else if (prop.access == SymbolAccessibility.PROTECTED &&
                             prop.set_accessor.access != SymbolAccessibility.PUBLIC)
                    {
                        access = prop.set_accessor.access;
                    }
                }
                else
                {
                    if (prop.get_accessor == null)
                    {
                        error = true;
                        Report.error(source_reference, "Property `%s' is write-only".printf(prop.get_full_name()));
                        return(false);
                    }
                    if (prop.access == SymbolAccessibility.PUBLIC)
                    {
                        access = prop.get_accessor.access;
                    }
                    else if (prop.access == SymbolAccessibility.PROTECTED &&
                             prop.get_accessor.access != SymbolAccessibility.PUBLIC)
                    {
                        access = prop.get_accessor.access;
                    }
                }
                instance = (prop.binding == MemberBinding.INSTANCE);

                // do not allow access to properties of generic types
                // if instance type does not specify type arguments
                if (prop.property_type is GenericType)
                {
                    generics = true;
                }
            }
            else if (member is Signal)
            {
                instance = true;
                access   = member.access;
            }

            member.used = true;
            member.version.check(source_reference);

            if (access == SymbolAccessibility.PROTECTED)
            {
                var target_type = (TypeSymbol)member.parent_symbol;

                bool in_subtype = false;
                for (Symbol this_symbol = context.analyzer.current_symbol; this_symbol != null; this_symbol = this_symbol.parent_symbol)
                {
                    if (this_symbol == target_type)
                    {
                        // required for interfaces with non-abstract methods
                        // accessing protected interface members
                        in_subtype = true;
                        break;
                    }

                    var cl = this_symbol as Class;
                    if (cl != null && cl.is_subtype_of(target_type))
                    {
                        in_subtype = true;
                        break;
                    }
                }

                if (!in_subtype)
                {
                    error = true;
                    Report.error(source_reference, "Access to protected member `%s' denied".printf(member.get_full_name()));
                    return(false);
                }
            }
            else if (access == SymbolAccessibility.PRIVATE)
            {
                var target_type = member.parent_symbol;

                bool in_target_type = false;
                for (Symbol this_symbol = context.analyzer.current_symbol; this_symbol != null; this_symbol = this_symbol.parent_symbol)
                {
                    if (target_type == this_symbol)
                    {
                        in_target_type = true;
                        break;
                    }
                }

                if (!in_target_type)
                {
                    error = true;
                    Report.error(source_reference, "Access to private member `%s' denied".printf(member.get_full_name()));
                    return(false);
                }
            }

            if (generics && inner != null)
            {
                var instance_type = inner.value_type;
                var pointer_type  = inner.value_type as PointerType;
                if (pointer_type != null)
                {
                    instance_type = pointer_type.base_type;
                }

                // instance type might be a subtype of the parent symbol of the member
                // that subtype might not be generic, so do not report an error in that case
                var object_type = instance_type as ObjectType;
                if (object_type != null && object_type.type_symbol.get_type_parameters().Count > 0 &&
                    instance_type.get_type_arguments().Count == 0)
                {
                    error = true;
                    Report.error(inner.source_reference, "missing generic type arguments");
                    return(false);
                }
            }

            if ((instance && !may_access_instance_members) ||
                (klass && !may_access_klass_members))
            {
                prototype_access = true;

                if (symbol_reference is Method)
                {
                    // also set static type for prototype access
                    // required when using instance methods as delegates in constants
                    // TODO replace by MethodPrototype
                    value_type = context.analyzer.get_value_type_for_symbol(symbol_reference, lvalue);
                }
                else if (symbol_reference is Field)
                {
                    value_type = new FieldPrototype((Field)symbol_reference);
                }
                else
                {
                    value_type = new InvalidType();
                }

                if (target_type != null)
                {
                    value_type.value_owned = target_type.value_owned;
                }
            }
            else
            {
                // implicit this access
                if (instance && inner == null)
                {
                    inner                        = new MemberAccess(null, "this", source_reference);
                    inner.value_type             = this_parameter.variable_type.copy();
                    inner.value_type.value_owned = false;
                    inner.symbol_reference       = this_parameter;
                }
                else
                {
                    check_lvalue_access();
                }

                if (!instance && !klass && !(symbol_reference is CreationMethod) && may_access_instance_members && inner != null)
                {
                    if (inner.symbol_reference is Method)
                    {
                        // do not warn when calling .begin or .end on static async method
                    }
                    else
                    {
                        Report.warning(source_reference, "Access to static member `%s' with an instance reference".printf(symbol_reference.get_full_name()));
                    }
                }

                if (context.experimental_non_null && instance && inner.value_type.nullable &&
                    !(inner.value_type is PointerType) && !(inner.value_type is GenericType) &&
                    !(inner.value_type is ArrayType))
                {
                    Report.error(source_reference, "Access to instance member `%s' from nullable reference denied".printf(symbol_reference.get_full_name()));
                }

                var m        = symbol_reference as Method;
                var inner_ma = inner as MemberAccess;
                if (m != null && m.binding == MemberBinding.STATIC && m.parent_symbol is ObjectTypeSymbol &&
                    inner != null && inner.value_type == null && inner_ma.type_argument_list.Count > 0)
                {
                    // support static methods in generic classes
                    inner.value_type = new ObjectType((ObjectTypeSymbol)m.parent_symbol);

                    foreach (var type_argument in inner_ma.type_argument_list)
                    {
                        inner.value_type.add_type_argument(type_argument);
                    }
                }

                formal_value_type = context.analyzer.get_value_type_for_symbol(symbol_reference, lvalue);
                if (inner != null && formal_value_type != null)
                {
                    value_type = formal_value_type.get_actual_type(inner.value_type, null, this);
                }
                else
                {
                    value_type = formal_value_type;
                }

                if (symbol_reference is Method)
                {
                    var method = (Method)symbol_reference;
                    if (target_type != null)
                    {
                        value_type.value_owned = target_type.value_owned;
                    }
                    if (instance && method.parent_symbol is TypeSymbol)
                    {
                        inner.target_type             = SemanticAnalyzer.get_data_type_for_symbol((TypeSymbol)method.parent_symbol);
                        inner.target_type.value_owned = method.this_parameter.variable_type.value_owned;
                    }
                }
                else if (symbol_reference is Property)
                {
                    var prop = (Property)symbol_reference;
                    if (instance && prop.parent_symbol != null)
                    {
                        inner.target_type = SemanticAnalyzer.get_data_type_for_symbol((TypeSymbol)prop.parent_symbol);
                    }
                }
                else if ((symbol_reference is Field ||
                          symbol_reference is Signal) &&
                         instance && symbol_reference.parent_symbol != null)
                {
                    var parent_type = SemanticAnalyzer.get_data_type_for_symbol((TypeSymbol)symbol_reference.parent_symbol);
                    inner.target_type = parent_type.get_actual_type(inner.value_type, null, this);
                }
            }

            return(!error);
        }
Example #4
0
        public static MemberAccess simple(string member_name, SourceReference source_reference = null)
        {
            MemberAccess @this = new MemberAccess(null, member_name, source_reference);

            return(@this);
        }
Example #5
0
        public override bool check(CodeContext context)
        {
            if (is_checked)
            {
                return(!error);
            }

            is_checked = true;

            if (Operator == UnaryOperator.REF || Operator == UnaryOperator.OUT)
            {
                inner.lvalue      = true;
                inner.target_type = target_type;
            }
            else if (Operator == UnaryOperator.INCREMENT || Operator == UnaryOperator.DECREMENT)
            {
                inner.lvalue = true;
            }

            if (!inner.check(context))
            {
                /* if there was an error in the inner expression, skip type check */
                error = true;
                return(false);
            }

            if (inner.value_type is FieldPrototype)
            {
                error = true;
                Report.error(inner.source_reference, "Access to instance member `%s' denied".printf(inner.symbol_reference.get_full_name()));
                return(false);
            }

            if (Operator == UnaryOperator.PLUS || Operator == UnaryOperator.MINUS)
            {
                // integer or floating point type
                if (!is_numeric_type(inner.value_type))
                {
                    error = true;
                    Report.error(source_reference, "Operator not supported for `%s'".printf(inner.value_type.ToString()));
                    return(false);
                }

                value_type = inner.value_type;
            }
            else if (Operator == UnaryOperator.LOGICAL_NEGATION)
            {
                // boolean type
                if (!inner.value_type.compatible(context.analyzer.bool_type))
                {
                    error = true;
                    Report.error(source_reference, "Operator not supported for `%s'".printf(inner.value_type.ToString()));
                    return(false);
                }

                value_type = inner.value_type;
            }
            else if (Operator == UnaryOperator.BITWISE_COMPLEMENT)
            {
                // integer type
                if (!is_integer_type(inner.value_type) && !(inner.value_type is EnumValueType))
                {
                    error = true;
                    Report.error(source_reference, "Operator not supported for `%s'".printf(inner.value_type.ToString()));
                    return(false);
                }

                value_type = inner.value_type;
            }
            else if (Operator == UnaryOperator.INCREMENT ||
                     Operator == UnaryOperator.DECREMENT)
            {
                // integer type
                if (!is_integer_type(inner.value_type))
                {
                    error = true;
                    Report.error(source_reference, "Operator not supported for `%s'".printf(inner.value_type.ToString()));
                    return(false);
                }

                var ma = find_member_access(inner);
                if (ma == null)
                {
                    error = true;
                    Report.error(source_reference, "Prefix operators not supported for this expression");
                    return(false);
                }

                var old_value = new MemberAccess(ma.inner, ma.member_name, inner.source_reference);
                var bin       = new BinaryExpression(Operator == UnaryOperator.INCREMENT ? BinaryOperator.PLUS : BinaryOperator.MINUS, old_value, new IntegerLiteral("1"), source_reference);

                var assignment = new Assignment(ma, bin, AssignmentOperator.SIMPLE, source_reference);
                assignment.target_type = target_type;
                context.analyzer.replaced_nodes.Add(this);
                parent_node.replace_expression(this, assignment);
                assignment.check(context);
                return(true);
            }
            else if (Operator == UnaryOperator.REF || Operator == UnaryOperator.OUT)
            {
                var ea = inner as ElementAccess;
                if (inner.symbol_reference is Field || inner.symbol_reference is Parameter || inner.symbol_reference is LocalVariable ||
                    (ea != null && ea.container.value_type is ArrayType))
                {
                    // ref and out can only be used with fields, parameters, local variables, and array element access
                    lvalue     = true;
                    value_type = inner.value_type;
                }
                else
                {
                    error = true;
                    Report.error(source_reference, "ref and out method arguments can only be used with fields, parameters, local variables, and array element access");
                    return(false);
                }
            }
            else
            {
                error = true;
                Report.error(source_reference, "internal error: unsupported unary operator");
                return(false);
            }

            return(!error);
        }