1/* valacastexpression.vala 2 * 3 * Copyright (C) 2006-2011 Jürg Billeter 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * Author: 20 * Jürg Billeter <j@bitron.ch> 21 */ 22 23 24/** 25 * Represents a type cast in the source code. 26 */ 27public class Vala.CastExpression : Expression { 28 /** 29 * The expression to be cast. 30 */ 31 public Expression inner { 32 get { 33 return _inner; 34 } 35 set { 36 _inner = value; 37 _inner.parent_node = this; 38 } 39 } 40 41 /** 42 * The target type. 43 */ 44 public DataType type_reference { 45 get { return _data_type; } 46 set { 47 _data_type = value; 48 _data_type.parent_node = this; 49 } 50 } 51 52 /** 53 * Checked casts return NULL instead of raising an error. 54 */ 55 public bool is_silent_cast { get; private set; } 56 57 public bool is_non_null_cast { get; private set; } 58 59 private Expression _inner; 60 61 private DataType _data_type; 62 63 /** 64 * Creates a new cast expression. 65 * 66 * @param inner expression to be cast 67 * @param type_reference target type 68 * @return newly created cast expression 69 */ 70 public CastExpression (Expression inner, DataType type_reference, SourceReference? source_reference = null) { 71 this.type_reference = type_reference; 72 this.source_reference = source_reference; 73 this.is_silent_cast = false; 74 this.is_non_null_cast = false; 75 this.inner = inner; 76 } 77 78 public CastExpression.silent (Expression inner, DataType type_reference, SourceReference? source_reference = null) { 79 this.type_reference = type_reference; 80 this.source_reference = source_reference; 81 this.is_silent_cast = true; 82 this.is_non_null_cast = false; 83 this.inner = inner; 84 } 85 86 public CastExpression.non_null (Expression inner, SourceReference? source_reference = null) { 87 this.inner = inner; 88 this.is_non_null_cast = true; 89 this.source_reference = source_reference; 90 } 91 92 public override void accept (CodeVisitor visitor) { 93 visitor.visit_cast_expression (this); 94 95 visitor.visit_expression (this); 96 } 97 98 public override void accept_children (CodeVisitor visitor) { 99 inner.accept (visitor); 100 if (!is_non_null_cast) { 101 type_reference.accept (visitor); 102 } 103 } 104 105 public override string to_string () { 106 if (is_non_null_cast) { 107 return "(!) %s".printf (inner.to_string ()); 108 } else if (is_silent_cast) { 109 return "%s as %s".printf (inner.to_string (), type_reference.to_string ()); 110 } else { 111 return "(%s) %s".printf (type_reference.to_string (), inner.to_string ()); 112 } 113 } 114 115 public override void replace_expression (Expression old_node, Expression new_node) { 116 if (inner == old_node) { 117 inner = new_node; 118 } 119 } 120 121 public override bool is_pure () { 122 return inner.is_pure (); 123 } 124 125 public override bool is_accessible (Symbol sym) { 126 return inner.is_accessible (sym); 127 } 128 129 public override void replace_type (DataType old_type, DataType new_type) { 130 if (type_reference == old_type) { 131 type_reference = new_type; 132 } 133 } 134 135 public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) { 136 inner.get_error_types (collection, source_reference); 137 } 138 139 public override bool check (CodeContext context) { 140 if (checked) { 141 return !error; 142 } 143 144 checked = true; 145 146 if (!inner.check (context)) { 147 error = true; 148 return false; 149 } 150 151 if (inner.value_type == null) { 152 Report.error (source_reference, "Invalid cast expression"); 153 error = true; 154 return false; 155 } 156 157 if (is_non_null_cast) { 158 // (!) non-null cast 159 type_reference = inner.value_type.copy (); 160 type_reference.nullable = false; 161 } 162 163 type_reference.check (context); 164 165 // FIXME: check whether cast is allowed 166 167 if (type_reference is DelegateType && inner.value_type is MethodType) { 168 if (target_type != null) { 169 inner.value_type.value_owned = target_type.value_owned; 170 } else { 171 inner.value_type.value_owned = true; 172 } 173 } 174 175 // Implicit transformation of stack-allocated value to heap-allocated boxed-type 176 if (!(is_silent_cast || is_non_null_cast) 177 && (type_reference is ValueType && type_reference.nullable) 178 && inner.value_type.is_non_null_simple_type ()) { 179 var local = new LocalVariable (type_reference, get_temp_name (), null, inner.source_reference); 180 var decl = new DeclarationStatement (local, source_reference); 181 182 insert_statement (context.analyzer.insert_block, decl); 183 184 var temp_access = SemanticAnalyzer.create_temp_access (local, target_type); 185 temp_access.formal_target_type = formal_target_type; 186 187 // don't set initializer earlier as this changes parent_node and parent_statement 188 local.initializer = inner; 189 decl.check (context); 190 191 context.analyzer.replaced_nodes.add (this); 192 parent_node.replace_expression (this, temp_access); 193 return temp_access.check (context); 194 } 195 196 value_type = type_reference; 197 value_type.value_owned = inner.value_type.value_owned; 198 value_type.floating_reference = inner.value_type.floating_reference; 199 200 if (is_silent_cast) { 201 value_type.nullable = true; 202 } 203 204 if (context.profile == Profile.GOBJECT 205 && is_gvariant (context, inner.value_type) && !is_gvariant (context, value_type)) { 206 // GVariant unboxing returns owned value 207 value_type.value_owned = true; 208 if (value_type.get_type_signature () == null) { 209 error = true; 210 Report.error (source_reference, "Casting of `GLib.Variant' to `%s' is not supported".printf (value_type.to_qualified_string ())); 211 } 212 } 213 214 if (context.profile == Profile.GOBJECT 215 && is_gvalue (context, inner.value_type) && !is_gvalue (context, value_type)) { 216 // GValue unboxing returns unowned value 217 value_type.value_owned = false; 218 if (value_type.nullable && value_type.type_symbol != null && !value_type.type_symbol.is_reference_type ()) { 219 error = true; 220 Report.error (source_reference, "Casting of `GLib.Value' to `%s' is not supported".printf (value_type.to_qualified_string ())); 221 } 222 } 223 224 inner.target_type = inner.value_type.copy (); 225 226 return !error; 227 } 228 229 bool is_gvariant (CodeContext context, DataType type) { 230 return type.type_symbol != null && type.type_symbol.is_subtype_of (context.analyzer.gvariant_type.type_symbol); 231 } 232 233 bool is_gvalue (CodeContext context, DataType type) { 234 return type.type_symbol != null && type.type_symbol.is_subtype_of (context.analyzer.gvalue_type.type_symbol); 235 } 236 237 public override void emit (CodeGenerator codegen) { 238 inner.emit (codegen); 239 240 codegen.visit_cast_expression (this); 241 242 codegen.visit_expression (this); 243 } 244 245 public override void get_defined_variables (Collection<Variable> collection) { 246 inner.get_defined_variables (collection); 247 } 248 249 public override void get_used_variables (Collection<Variable> collection) { 250 inner.get_used_variables (collection); 251 } 252 253 public override bool is_constant () { 254 return inner.is_constant (); 255 } 256} 257