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); }
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); }
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); }