+2008-04-14 Jürg Billeter <j@bitron.ch>
+
+ * vala/Makefile.am, vala/valaarraytype.vala,
+ vala/valaclasstype.vala, vala/valacodenode.vala,
+ vala/valadatatype.vala, vala/valaerrortype.vala,
+ vala/valaintegertype.vala, vala/valainterfacetype.vala,
+ vala/valamethodtype.vala, vala/valanullchecker.vala,
+ vala/valanulltype.vala, vala/valaparser.vala,
+ vala/valapointertype.vala, vala/valasemanticanalyzer.vala,
+ vala/valasymbolresolver.vala, vala/valatypeparametertype.vala,
+ vala/valaunresolvedtype.vala, vala/valavaluetype.vala,
+ gobject/valaccodegenerator.vala, compiler/valacompiler.vala,
+ vapigen/valagidlparser.vala: move experimental null reference
+ checking to separate visitor
+
2008-04-14 Marc-Andre Lureau <marcandre.lureau@gmail.com>
* vapi/glib-2.0.vapi (Value): renamed register_transform to
return quit ();
}
+ if (context.non_null_experimental) {
+ var null_checker = new NullChecker ();
+ null_checker.check (context);
+
+ if (Report.get_errors () > 0) {
+ return quit ();
+ }
+ }
+
var memory_manager = new MemoryManager ();
memory_manager.analyze (context);
// g_free (NULL) is allowed
bool uses_gfree = (type.data_type != null && !type.data_type.is_reference_counting () && type.data_type.get_free_function () == "g_free");
uses_gfree = uses_gfree || type is ArrayType;
- if ((context.non_null_experimental && !type.requires_null_check) || uses_gfree) {
+ if (uses_gfree) {
return new CCodeParenthesizedExpression (cassign);
}
var ccall = new CCodeFunctionCall (dupexpr);
- if (((context.non_null_experimental && !expr.static_type.requires_null_check) && expr.static_type.type_parameter == null) || expr is StringLiteral) {
+ if (expr is StringLiteral) {
// expression is non-null
ccall.add_argument ((CCodeExpression) expr.ccodenode);
valanamedargument.vala \
valanamespace.vala \
valanamespacereference.vala \
+ valanullchecker.vala \
valanullliteral.vala \
valanulltype.vala \
valaobjectcreationexpression.vala \
result.takes_ownership = takes_ownership;
result.is_out = is_out;
result.nullable = nullable;
- result.requires_null_check = requires_null_check;
result.floating_reference = floating_reference;
result.is_ref = is_ref;
return element_type.to_string () + "[]";
}
- public override bool compatible (DataType target_type, bool enable_non_null) {
+ public override bool compatible (DataType target_type) {
if (target_type is PointerType || (target_type.data_type != null && target_type.data_type.get_attribute ("PointerType") != null)) {
/* any array type can be cast to a generic pointer */
return true;
if (element_type.compatible (target_array_type.element_type)
&& target_array_type.element_type.compatible (element_type)) {
- if (requires_null_check && !target_type.nullable) {
- // incompatibility between null and non-null types
- return !enable_non_null;
- }
return true;
}
result.takes_ownership = takes_ownership;
result.is_out = is_out;
result.nullable = nullable;
- result.requires_null_check = requires_null_check;
result.floating_reference = floating_reference;
result.is_ref = is_ref;
/**
* Parent of this code node.
*/
- public weak CodeNode parent_node { get; set; }
+ public weak CodeNode? parent_node { get; set; }
/**
* References the location in the source file where this code node has
* been written.
*/
- public SourceReference source_reference { get; set; }
+ public SourceReference? source_reference { get; set; }
/**
* Contains all attributes that have been specified for this code node.
/**
* Generated CCodeNode that corresponds to this code node.
*/
- public CCodeNode ccodenode {
+ public CCodeNode? ccodenode {
get {
return _ccodenode;
}
/**
* Binding to the generated code.
*/
- public CodeBinding code_binding { get; set; }
+ public CodeBinding? code_binding { get; set; }
/**
* Specifies whether a fatal error has been detected in this code node.
return null;
}
- private CCodeNode _ccodenode;
+ private CCodeNode? _ccodenode;
/**
* Returns a string that represents this code node.
public bool nullable { get; set; }
/**
- * Specifies that the expression may not be dereferenced without
- * prior null check.
- */
- public bool requires_null_check { get; set; }
-
- /**
* The referred data type.
*/
public weak Typesymbol data_type { get; set; }
}
}
- public virtual bool compatible (DataType target_type, bool enable_non_null = true) {
+ public virtual bool compatible (DataType target_type) {
if (target_type is DelegateType && this is DelegateType) {
return ((DelegateType) target_type).delegate_symbol == ((DelegateType) this).delegate_symbol;
}
}
if (data_type == target_type.data_type) {
- if (requires_null_check && !target_type.nullable && data_type != null && data_type.is_reference_type ()) {
- // incompatibility between null and non-null types
- return !enable_non_null;
- }
return true;
}
}
if (data_type != null && target_type.data_type != null && data_type.is_subtype_of (target_type.data_type)) {
- if (requires_null_check && !target_type.nullable && data_type.is_reference_type ()) {
- // incompatibility between null and non-null types
- return !enable_non_null;
- }
return true;
}
this.source_reference = source_reference;
}
- public override bool compatible (DataType target_type, bool enable_non_null = true) {
+ public override bool compatible (DataType target_type) {
/* temporarily ignore type parameters */
if (target_type.type_parameter != null) {
return true;
return type;
}
- public override bool compatible (DataType target_type, bool enable_non_null = true) {
+ public override bool compatible (DataType target_type) {
if (target_type.data_type is Struct && literal.get_type_name () == "int") {
// int literals are implicitly convertible to integer types
// of a lower rank if the value of the literal is within
}
}
- return base.compatible (target_type, enable_non_null);
+ return base.compatible (target_type);
}
}
result.takes_ownership = takes_ownership;
result.is_out = is_out;
result.nullable = nullable;
- result.requires_null_check = requires_null_check;
result.floating_reference = floating_reference;
result.is_ref = is_ref;
return new MethodType (method_symbol);
}
- public override bool compatible (DataType target_type, bool enable_non_null = true) {
+ public override bool compatible (DataType target_type) {
var dt = target_type as DelegateType;
if (dt == null) {
// method types incompatible to anything but delegates
--- /dev/null
+/* valanullchecker.vala
+ *
+ * Copyright (C) 2008 Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Jürg Billeter <j@bitron.ch>
+ */
+
+using GLib;
+
+/**
+ * Code visitor checking null references.
+ */
+public class Vala.NullChecker : CodeVisitor {
+ private CodeContext context;
+
+ DataType current_return_type;
+
+ public NullChecker () {
+ }
+
+ public void check (CodeContext context) {
+ this.context = context;
+
+ context.accept (this);
+ }
+
+ void check_compatible (Expression expr, DataType target_type) {
+ if (!target_type.nullable) {
+ if (expr.static_type is NullType) {
+ Report.error (expr.source_reference, "`null' incompatible with `%s'".printf (target_type.to_string ()));
+ } else if (expr.static_type.nullable) {
+ Report.warning (expr.source_reference, "`%s' incompatible with `%s'".printf (expr.static_type.to_string (), target_type.to_string ()));
+ }
+ }
+ }
+
+ void check_non_null (Expression expr) {
+ if (expr.static_type is NullType) {
+ Report.error (expr.source_reference, "null dereference");
+ } else if (expr.static_type.nullable) {
+ Report.warning (expr.source_reference, "possible null dereference");
+ }
+ }
+
+ public override void visit_source_file (SourceFile file) {
+ file.accept_children (this);
+ }
+
+ public override void visit_class (Class cl) {
+ cl.accept_children (this);
+ }
+
+ public override void visit_struct (Struct st) {
+ st.accept_children (this);
+ }
+
+ public override void visit_interface (Interface iface) {
+ iface.accept_children (this);
+ }
+
+ public override void visit_enum (Enum en) {
+ en.accept_children (this);
+ }
+
+ public override void visit_field (Field f) {
+ f.accept_children (this);
+ }
+
+ public override void visit_method (Method m) {
+ var old_return_type = current_return_type;
+ current_return_type = m.return_type;
+
+ m.accept_children (this);
+
+ current_return_type = old_return_type;
+ }
+
+ public override void visit_creation_method (CreationMethod m) {
+ m.accept_children (this);
+ }
+
+ public override void visit_formal_parameter (FormalParameter p) {
+ p.accept_children (this);
+
+ if (p.default_expression != null) {
+ check_compatible (p.default_expression, p.type_reference);
+ }
+ }
+
+ public override void visit_property (Property prop) {
+ prop.accept_children (this);
+ }
+
+ public override void visit_property_accessor (PropertyAccessor acc) {
+ acc.accept_children (this);
+ }
+
+ public override void visit_constructor (Constructor c) {
+ c.accept_children (this);
+ }
+
+ public override void visit_destructor (Destructor d) {
+ d.accept_children (this);
+ }
+
+ public override void visit_block (Block b) {
+ b.accept_children (this);
+ }
+
+ public override void visit_variable_declarator (VariableDeclarator decl) {
+ decl.accept_children (this);
+
+ if (decl.initializer != null) {
+ check_compatible (decl.initializer, decl.type_reference);
+ }
+ }
+
+ public override void visit_if_statement (IfStatement stmt) {
+ stmt.accept_children (this);
+
+ check_non_null (stmt.condition);
+ }
+
+ public override void visit_switch_section (SwitchSection section) {
+ section.accept_children (this);
+ }
+
+ public override void visit_while_statement (WhileStatement stmt) {
+ stmt.accept_children (this);
+
+ check_non_null (stmt.condition);
+ }
+
+ public override void visit_do_statement (DoStatement stmt) {
+ stmt.accept_children (this);
+
+ check_non_null (stmt.condition);
+ }
+
+ public override void visit_for_statement (ForStatement stmt) {
+ stmt.accept_children (this);
+
+ check_non_null (stmt.condition);
+ }
+
+ public override void visit_foreach_statement (ForeachStatement stmt) {
+ stmt.accept_children (this);
+
+ check_non_null (stmt.collection);
+ }
+
+ public override void visit_return_statement (ReturnStatement stmt) {
+ stmt.accept_children (this);
+
+ if (stmt.return_expression != null) {
+ check_compatible (stmt.return_expression, current_return_type);
+ }
+ }
+
+ public override void visit_throw_statement (ThrowStatement stmt) {
+ stmt.accept_children (this);
+
+ check_non_null (stmt.error_expression);
+ }
+
+ public override void visit_try_statement (TryStatement stmt) {
+ stmt.accept_children (this);
+ }
+
+ public override void visit_catch_clause (CatchClause clause) {
+ clause.accept_children (this);
+ }
+
+ public override void visit_delete_statement (DeleteStatement stmt) {
+ stmt.accept_children (this);
+ }
+
+ public override void visit_invocation_expression (InvocationExpression expr) {
+ expr.accept_children (this);
+
+ var mtype = expr.call.static_type as MethodType;
+ var ma = expr.call as MemberAccess;
+ if (mtype != null && mtype.method_symbol.instance && ma != null) {
+ if (ma.inner == null) {
+ // implicit this call, always non-null
+ } else {
+ check_non_null (ma.inner);
+ }
+ }
+ }
+
+ public override void visit_element_access (ElementAccess expr) {
+ check_non_null (expr.container);
+ }
+
+ public override void visit_postfix_expression (PostfixExpression expr) {
+ check_non_null (expr.inner);
+ }
+
+ public override void visit_unary_expression (UnaryExpression expr) {
+ switch (expr.operator) {
+ case UnaryOperator.PLUS:
+ case UnaryOperator.MINUS:
+ case UnaryOperator.LOGICAL_NEGATION:
+ case UnaryOperator.BITWISE_COMPLEMENT:
+ case UnaryOperator.INCREMENT:
+ case UnaryOperator.DECREMENT:
+ check_non_null (expr.inner);
+ break;
+ }
+ }
+
+ public override void visit_conditional_expression (ConditionalExpression expr) {
+ check_non_null (expr.condition);
+ }
+
+ public override void visit_lambda_expression (LambdaExpression l) {
+ l.accept_children (this);
+ }
+
+ public override void visit_assignment (Assignment a) {
+ a.accept_children (this);
+
+ check_compatible (a.right, a.left.static_type);
+ }
+}
this.source_reference = source_reference;
}
- public override bool compatible (DataType target_type, bool enable_non_null = true) {
+ public override bool compatible (DataType target_type) {
if (!(target_type is PointerType) && (target_type is NullType || (target_type.data_type == null && target_type.type_parameter == null))) {
return true;
}
if (target_type.data_type.is_reference_type () ||
target_type is ArrayType ||
target_type is DelegateType) {
- // incompatibility between null and non-null types
- return !enable_non_null;
+ return true;
}
/* null is not compatible with any other type (i.e. value types) */
type.pointer_level = stars;
type.array_rank = array_rank;
type.nullable = nullable;
- type.requires_null_check = nullable;
type.transfers_ownership = transfers_ownership;
return type;
}
if (!((UnresolvedType) type).is_weak) {
type.takes_ownership = true;
}
- type.requires_null_check = false;
- type.nullable = true;
decl = context.create_local_variable_declaration (type, get_src (begin));
foreach (VariableDeclarator var_decl in declarators) {
var_decl.type_reference = type.copy ();
return new PointerType (base_type);
}
- public override bool compatible (DataType target_type, bool enable_non_null = true) {
+ public override bool compatible (DataType target_type) {
if (target_type is PointerType || (target_type.data_type != null && target_type.data_type.get_attribute ("PointerType") != null)) {
return true;
}
if (base_type.is_reference_type_or_type_parameter ()) {
// Object* is compatible with Object if Object is a reference type
- return base_type.compatible (target_type, enable_non_null);
+ return base_type.compatible (target_type);
}
return false;
if (unary != null && (unary.operator == UnaryOperator.REF || unary.operator == UnaryOperator.OUT)) {
// TODO check type for ref and out expressions
} else if (!e.static_type.compatible (e.expected_type)) {
- if (!e.static_type.compatible (e.expected_type, false)) {
- error = true;
- e.error = true;
- Report.error (e.source_reference, "Expected initializer of type `%s' but got `%s'".printf (e.expected_type.to_string (), e.static_type.to_string ()));
- } else if (context.non_null_experimental) {
- Report.warning (e.source_reference, "Expected initializer of type `%s' but got `%s'".printf (e.expected_type.to_string (), e.static_type.to_string ()));
- }
+ error = true;
+ e.error = true;
+ Report.error (e.source_reference, "Expected initializer of type `%s' but got `%s'".printf (e.expected_type.to_string (), e.static_type.to_string ()));
}
}
var array_type = (ArrayType) collection_type;
element_data_type = array_type.element_type;
need_type_check = true;
- } else if (collection_type.compatible (glist_type, false) || collection_type.compatible (gslist_type, false)) {
+ } else if (collection_type.compatible (glist_type) || collection_type.compatible (gslist_type)) {
if (collection_type.get_type_arguments ().size > 0) {
element_data_type = (DataType) collection_type.get_type_arguments ().get (0);
need_type_check = true;
}
- } else if (iterable_type != null && collection_type.compatible (iterable_type, false)) {
+ } else if (iterable_type != null && collection_type.compatible (iterable_type)) {
stmt.iterator_variable_declarator = new VariableDeclarator ("%s_it".printf (stmt.variable_name));
stmt.iterator_variable_declarator.type_reference = new InterfaceType (iterator_type);
stmt.iterator_variable_declarator.type_reference.takes_ownership = true;
if (stmt.return_expression != null &&
!stmt.return_expression.static_type.compatible (current_return_type)) {
- if (!stmt.return_expression.static_type.compatible (current_return_type, false)) {
- Report.error (stmt.source_reference, "Return: Cannot convert from `%s' to `%s'".printf (stmt.return_expression.static_type.to_string (), current_return_type.to_string ()));
- return;
- } else if (context.non_null_experimental) {
- Report.warning (stmt.source_reference, "Return value may not be null");
- }
+ Report.error (stmt.source_reference, "Return: Cannot convert from `%s' to `%s'".printf (stmt.return_expression.static_type.to_string (), current_return_type.to_string ()));
+ return;
}
if (stmt.return_expression != null &&
return false;
}
} else if (!arg.static_type.compatible (param.type_reference)) {
- if (!arg.static_type.compatible (param.type_reference, false)) {
- expr.error = true;
- Report.error (expr.source_reference, "Argument %d: Cannot convert from `%s' to `%s'".printf (i + 1, arg.static_type.to_string (), param.type_reference.to_string ()));
- return false;
- } else if (context.non_null_experimental) {
- Report.warning (expr.source_reference, "Argument %d: Argument may not be null".printf (i + 1));
- }
+ expr.error = true;
+ Report.error (expr.source_reference, "Argument %d: Cannot convert from `%s' to `%s'".printf (i + 1, arg.static_type.to_string (), param.type_reference.to_string ()));
+ return false;
} else {
// 0 => null, 1 => in, 2 => ref, 3 => out
int arg_type = 1;
if (!expr.right.static_type.compatible (expr.left.static_type)
&& !expr.left.static_type.compatible (expr.right.static_type)) {
- if (!expr.right.static_type.compatible (expr.left.static_type, false)
- && !expr.left.static_type.compatible (expr.right.static_type, false)) {
- Report.error (expr.source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (expr.right.static_type.to_string (), expr.left.static_type.to_string ()));
- expr.error = true;
- return;
- } else if (context.non_null_experimental) {
- // warn about incompatibility between null and non-null types
- Report.warning (expr.source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (expr.right.static_type.to_string (), expr.left.static_type.to_string ()));
- }
+ Report.error (expr.source_reference, "Equality operation: `%s' and `%s' are incompatible".printf (expr.right.static_type.to_string (), expr.left.static_type.to_string ()));
+ expr.error = true;
+ return;
}
if (expr.left.static_type.compatible (string_type)
* i.e. a.{left|right}.static_type == null, skip type check */
if (!a.right.static_type.compatible (a.left.static_type)) {
- if (!a.right.static_type.compatible (a.left.static_type, false)) {
- a.error = true;
- Report.error (a.source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (a.right.static_type.to_string (), a.left.static_type.to_string ()));
- return;
- } else if (context.non_null_experimental) {
- // warn about incompatibility between null and non-null types
- Report.warning (a.source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (a.right.static_type.to_string (), a.left.static_type.to_string ()));
- }
+ a.error = true;
+ Report.error (a.source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (a.right.static_type.to_string (), a.left.static_type.to_string ()));
+ return;
}
if (a.right.static_type.transfers_ownership) {
var ea = (ElementAccess) a.left;
if (!a.right.static_type.compatible (a.left.static_type)) {
- if (!a.right.static_type.compatible (a.left.static_type, false)) {
- a.error = true;
- Report.error (a.source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (a.right.static_type.to_string (), a.left.static_type.to_string ()));
- return;
- } else if (context.non_null_experimental) {
- // warn about incompatibility between null and non-null types
- Report.warning (a.source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (a.right.static_type.to_string (), a.left.static_type.to_string ()));
- }
+ a.error = true;
+ Report.error (a.source_reference, "Assignment: Cannot convert from `%s' to `%s'".printf (a.right.static_type.to_string (), a.left.static_type.to_string ()));
+ return;
}
if (a.right.static_type.transfers_ownership) {
type.is_ref = unresolved_type.is_ref;
type.is_out = unresolved_type.is_out;
type.nullable = unresolved_type.nullable;
- type.requires_null_check = unresolved_type.requires_null_check;
foreach (DataType type_arg in unresolved_type.get_type_arguments ()) {
type.add_type_argument (type_arg);
}
*/
type.takes_ownership = false;
type.transfers_ownership = false;
-
- /* reset nullable of value-types for local variables */
- if (type.nullable && !type.requires_null_check) {
- type.nullable = false;
- }
}
/* check for array */
element_type.is_ref = false;
element_type.is_out = false;
element_type.nullable = false;
- element_type.requires_null_check = false;
type = new ArrayType (element_type, unresolved_type.array_rank, unresolved_type.source_reference);
type.add_type_argument (element_type);
type.is_ref = unresolved_type.is_ref;
type.is_out = unresolved_type.is_out;
type.nullable = unresolved_type.nullable;
- type.requires_null_check = unresolved_type.nullable;
}
return type;
public override void visit_variable_declarator (VariableDeclarator decl) {
decl.accept_children (this);
+ if (decl.type_reference is ReferenceType) {
+ decl.type_reference.nullable = true;
+ }
}
public override void visit_initializer_list (InitializerList list) {
result.takes_ownership = takes_ownership;
result.is_out = is_out;
result.nullable = nullable;
- result.requires_null_check = requires_null_check;
result.floating_reference = floating_reference;
result.is_ref = is_ref;
result.takes_ownership = takes_ownership;
result.is_out = is_out;
result.nullable = nullable;
- result.requires_null_check = requires_null_check;
result.unresolved_symbol = unresolved_symbol.copy ();
result.array_rank = array_rank;
result.pointer_level = pointer_level;
result.takes_ownership = takes_ownership;
result.is_out = is_out;
result.nullable = nullable;
- result.requires_null_check = requires_null_check;
result.floating_reference = floating_reference;
result.is_ref = is_ref;
} else if (nv[0] == "nullable") {
if (eval (nv[1]) == "1") {
param_type.nullable = true;
- param_type.requires_null_check = true;
}
} else if (nv[0] == "transfer_ownership") {
if (eval (nv[1]) == "1") {
} else if (nv[0] == "nullable") {
if (eval (nv[1]) == "1") {
param_type.nullable = true;
- param_type.requires_null_check = true;
}
} else if (nv[0] == "type_name") {
param_type.unresolved_symbol = new UnresolvedSymbol (null, eval (nv[1]));