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); }
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); }
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); }
public static MemberAccess simple(string member_name, SourceReference source_reference = null) { MemberAccess @this = new MemberAccess(null, member_name, source_reference); return(@this); }
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); }