Browse Source

Add constant literal expressions

gingerBill 5 years ago
parent
commit
f99f351e01
5 changed files with 284 additions and 42 deletions
  1. 55 0
      examples/demo/demo.odin
  2. 1 1
      src/check_decl.cpp
  3. 212 40
      src/check_expr.cpp
  4. 7 0
      src/ir.cpp
  5. 9 1
      src/types.cpp

+ 55 - 0
examples/demo/demo.odin

@@ -1815,6 +1815,60 @@ soa_struct_layout :: proc() {
 	}
 }
 
+constant_literal_expressions :: proc() {
+	fmt.println("\n#constant literal expressions");
+
+	Bar :: struct {x, y: f32};
+	Foo :: struct {a, b: int, using c: Bar};
+
+	FOO_CONST :: Foo{b = 2, a = 1, c = {3, 4}};
+
+
+	fmt.println(FOO_CONST.a);
+	fmt.println(FOO_CONST.b);
+	fmt.println(FOO_CONST.c);
+	fmt.println(FOO_CONST.c.x);
+	fmt.println(FOO_CONST.c.y);
+	fmt.println(FOO_CONST.x); // using works as expected
+	fmt.println(FOO_CONST.y);
+
+	fmt.println("-------");
+
+	ARRAY_CONST :: [3]int{1 = 4, 2 = 9, 0 = 1};
+
+	fmt.println(ARRAY_CONST[0]);
+	fmt.println(ARRAY_CONST[1]);
+	fmt.println(ARRAY_CONST[2]);
+
+	fmt.println("-------");
+
+	FOO_ARRAY_DEFAULTS :: [3]Foo{{}, {}, {}};
+	fmt.println(FOO_ARRAY_DEFAULTS[2].x);
+
+
+
+	fmt.println("-------");
+
+	Baz :: enum{A=5, B, C, D=9};
+	ENUM_ARRAY_CONST :: [Baz]int{.A .. .C = 1, .D = 16};
+
+	fmt.println(ENUM_ARRAY_CONST[.A]);
+	fmt.println(ENUM_ARRAY_CONST[.B]);
+	fmt.println(ENUM_ARRAY_CONST[.C]);
+	fmt.println(ENUM_ARRAY_CONST[.D]);
+
+	fmt.println("-------");
+
+	STRING_CONST :: "Hellope!";
+
+	fmt.println(STRING_CONST[0]);
+	fmt.println(STRING_CONST[2]);
+	fmt.println(STRING_CONST[3]);
+
+	fmt.println(STRING_CONST[0:5]);
+	fmt.println(STRING_CONST[3:][:4]);
+}
+
 
 main :: proc() {
 	when true {
@@ -1844,5 +1898,6 @@ main :: proc() {
 		range_statements_with_multiple_return_values();
 		threading_example();
 		soa_struct_layout();
+		constant_literal_expressions();
 	}
 }

+ 1 - 1
src/check_decl.cpp

@@ -162,7 +162,7 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
 		return;
 	}
 
-#if 1
+#if 0
 	if (!is_type_constant_type(operand->type)) {
 		gbString type_str = type_to_string(operand->type);
 		error(operand->expr, "Invalid constant type: '%s'", type_str);

+ 212 - 40
src/check_expr.cpp

@@ -3108,6 +3108,8 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
 					return false;
 				}
 
+				if (value) *value = exact_value_to_i64(exact_value_sub(operand.value, lo));
+
 				return true;
 
 			} else { // NOTE(bill): Do array bound checking
@@ -3144,6 +3146,140 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
 	return true;
 }
 
+ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 index, bool *success_, bool *finish_) {
+	if (value.kind == ExactValue_String) {
+		GB_ASSERT(0 <= index && index < value.value_string.len);
+		u8 val = value.value_string[index];
+		if (success_) *success_ = true;
+		if (finish_) *finish_ = true;
+		return exact_value_u64(val);
+	}
+	if (value.kind != ExactValue_Compound) {
+		if (success_) *success_ = true;
+		if (finish_) *finish_ = true;
+		return value;
+	}
+
+
+	Ast *node = value.value_compound;
+	switch (node->kind) {
+	case_ast_node(cl, CompoundLit, node);
+		if (cl->elems.count == 0) {
+			if (success_) *success_ = true;
+			if (finish_) *finish_ = true;
+			return empty_exact_value;
+		}
+
+		if (cl->elems[0]->kind == Ast_FieldValue) {
+			if (is_type_struct(node->tav.type)) {
+				for_array(i, cl->elems) {
+					Ast *elem = cl->elems[i];
+					if (elem->kind != Ast_FieldValue) {
+						continue;
+					}
+					ast_node(fv, FieldValue, elem);
+					String name = fv->field->Ident.token.string;
+					Selection sub_sel = lookup_field(node->tav.type, name, false);
+					defer (array_free(&sub_sel.index));
+					if (sub_sel.index[0] == index) {
+						value = fv->value->tav.value;
+						break;
+					}
+				}
+			} else if (is_type_array(node->tav.type) || is_type_enumerated_array(node->tav.type)) {
+				for_array(i, cl->elems) {
+					Ast *elem = cl->elems[i];
+					if (elem->kind != Ast_FieldValue) {
+						continue;
+					}
+					ast_node(fv, FieldValue, elem);
+					if (is_ast_range(fv->field)) {
+						ast_node(ie, BinaryExpr, fv->field);
+						TypeAndValue lo_tav = ie->left->tav;
+						TypeAndValue hi_tav = ie->right->tav;
+						GB_ASSERT(lo_tav.mode == Addressing_Constant);
+						GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+						TokenKind op = ie->op.kind;
+						i64 lo = exact_value_to_i64(lo_tav.value);
+						i64 hi = exact_value_to_i64(hi_tav.value);
+
+						i64 corrected_index = index;
+
+						if (is_type_enumerated_array(node->tav.type)) {
+							Type *bt = base_type(node->tav.type);
+							GB_ASSERT(bt->kind == Type_EnumeratedArray);
+							corrected_index = index + exact_value_to_i64(bt->EnumeratedArray.min_value);
+						}
+						if (op == Token_Ellipsis) {
+							if (lo <= corrected_index && corrected_index <= hi) {
+								TypeAndValue tav = fv->value->tav;
+								if (success_) *success_ = true;
+								if (finish_) *finish_ = false;
+								return tav.value;
+							}
+						} else {
+							if (lo <= corrected_index && corrected_index < hi) {
+								TypeAndValue tav = fv->value->tav;
+								if (success_) *success_ = true;
+								if (finish_) *finish_ = false;
+								return tav.value;
+							}
+						}
+					} else {
+						TypeAndValue index_tav = fv->field->tav;
+						GB_ASSERT(index_tav.mode == Addressing_Constant);
+						ExactValue index_value = index_tav.value;
+						if (is_type_enumerated_array(node->tav.type)) {
+							Type *bt = base_type(node->tav.type);
+							GB_ASSERT(bt->kind == Type_EnumeratedArray);
+							index_value = exact_value_sub(index_value, bt->EnumeratedArray.min_value);
+						}
+
+						i64 field_index = exact_value_to_i64(index_value);
+						if (index == field_index) {
+							TypeAndValue tav = fv->value->tav;
+							value = tav.value;
+							break;
+						}
+					}
+
+				}
+			}
+		} else {
+			i32 count = (i32)cl->elems.count;
+			if (count < index) {
+				if (success_) *success_ = false;
+				if (finish_) *finish_ = true;
+				return empty_exact_value;
+			}
+			TypeAndValue tav = cl->elems[index]->tav;
+			if (tav.mode == Addressing_Constant) {
+				if (success_) *success_ = true;
+				if (finish_) *finish_ = false;
+				return tav.value;
+			} else {
+				GB_ASSERT(is_type_untyped_nil(tav.type));
+				if (success_) *success_ = true;
+				if (finish_) *finish_ = false;
+				return tav.value;
+			}
+		}
+
+	case_end;
+
+	default:
+		// TODO(bill): Should this be a general fallback?
+		if (success_) *success_ = true;
+		if (finish_) *finish_ = true;
+		return empty_exact_value;
+	}
+
+	if (finish_) *finish_ = false;
+	return value;
+}
+
+
 
 ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selection sel, bool *success_) {
 	if (operand->mode != Addressing_Constant) {
@@ -3169,39 +3305,11 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
 			i32 index = sel.index[0];
 			sel = sub_selection(sel, 1);
 
-			Ast *node = value.value_compound;
-			switch (node->kind) {
-			case_ast_node(cl, CompoundLit, node);
-				if (cl->elems.count == 0) {
-					if (success_) *success_ = true;
-					return empty_exact_value;
-				}
-
-				if (cl->elems[0]->kind == Ast_FieldValue) {
-					GB_PANIC("TODO");
-				} else {
-					i32 count = (i32)cl->elems.count;
-					if (count < index) {
-						if (success_) *success_ = false;
-						return empty_exact_value;
-					}
-					TypeAndValue tav = cl->elems[index]->tav;
-					if (tav.mode == Addressing_Constant) {
-						value = tav.value;
-					} else {
-						GB_ASSERT(is_type_untyped_nil(tav.type));
-						value = tav.value;
-					}
-				}
-
-			case_end;
-
-			default:
-				if (success_) *success_ = true;
-				return empty_exact_value;
+			bool finish = false;
+			value = get_constant_field_single(c, value, index, success_, &finish);
+			if (finish) {
+				return value;
 			}
-
-			depth += 1;
 		}
 
 		if (success_) *success_ = true;
@@ -3366,6 +3474,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 			operand->expr = node;
 			operand->value = field_value;
 			operand->type = entity->type;
+			add_entity_use(c, selector, entity);
+			add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
 			return entity;
 		}
 
@@ -3389,6 +3499,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 			operand->expr = node;
 			operand->value = field_value;
 			operand->type = entity->type;
+			add_entity_use(c, selector, entity);
+			add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
 			return entity;
 		}
 
@@ -7002,6 +7114,13 @@ bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count,
 			}
 			o->type = t_u8;
 			return true;
+		} else if (t->Basic.kind == Basic_UntypedString) {
+			if (o->mode == Addressing_Constant) {
+				*max_count = o->value.value_string.len;
+				o->type = t_u8;
+				return true;
+			}
+			return false;
 		}
 		break;
 
@@ -7733,6 +7852,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 								error(elem, "Expected a constant integer as an array field");
 								continue;
 							}
+							// add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
 
 							i64 index = exact_value_to_i64(op_index.value);
 
@@ -7913,13 +8033,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 
 
 						// NOTE(bill): These are sanity checks for invalid enum values
-						if (max_type_count >= 0 && (lo < total_lo || lo >= total_hi)) {
+						if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
 							gbString lo_str = expr_to_string(x.expr);
 							error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
 							gb_string_free(lo_str);
 							continue;
 						}
-						if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
+						if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
 							gbString hi_str = expr_to_string(y.expr);
 							error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
 							gb_string_free(hi_str);
@@ -7946,7 +8066,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 
 						i64 index = exact_value_to_i64(op_index.value);
 
-						if (max_type_count >= 0 && (index < total_lo || index >= total_hi)) {
+						if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
 							gbString idx_str = expr_to_string(op_index.expr);
 							error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
 							gb_string_free(idx_str);
@@ -8506,7 +8626,15 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 		bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
 
 		if (is_const) {
-			valid = false;
+			if (is_type_array(t)) {
+				// Okay
+			} else if (is_type_enumerated_array(t)) {
+				// Okay
+			} else if (is_type_string(t)) {
+				// Okay
+			} else {
+				valid = false;
+			}
 		}
 
 		if (!valid) {
@@ -8515,7 +8643,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			defer (gb_string_free(str));
 			defer (gb_string_free(type_str));
 			if (is_const) {
-				error(o->expr, "Cannot index a constant '%s'", str);
+				error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
 			} else {
 				error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
 			}
@@ -8542,6 +8670,20 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 
 		i64 index = 0;
 		bool ok = check_index_value(c, false, ie->index, max_count, &index, index_type_hint);
+		if (is_const) {
+			if (index < 0) {
+				gbString str = expr_to_string(o->expr);
+				error(o->expr, "Cannot index a constant '%s'", str);
+				gb_string_free(str);
+				o->mode = Addressing_Invalid;
+				o->expr = node;
+				return kind;
+			} else if (ok) {
+				ExactValue value = type_and_value_of_expr(ie->expr).value;
+				o->mode = Addressing_Constant;
+				o->value = get_constant_field_single(c, value, cast(i32)index, nullptr, nullptr);
+			}
+		}
 
 		node->viral_state_flags |= ie->index->viral_state_flags;
 	case_end;
@@ -8563,7 +8705,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 		Type *t = base_type(type_deref(o->type));
 		switch (t->kind) {
 		case Type_Basic:
-			if (t->Basic.kind == Basic_string) {
+			if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
 				valid = true;
 				if (o->mode == Addressing_Constant) {
 					max_count = o->value.value_string.len;
@@ -8651,6 +8793,36 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			}
 		}
 
+		if (is_type_string(t) && max_count >= 0) {
+			bool all_constant = true;
+			for (isize i = 0; i < gb_count_of(nodes); i++) {
+				if (nodes[i] != nullptr) {
+					TypeAndValue tav = type_and_value_of_expr(nodes[i]);
+					if (tav.mode != Addressing_Constant) {
+						all_constant = false;
+						break;
+					}
+				}
+			}
+			if (!all_constant) {
+				gbString str = expr_to_string(o->expr);
+				error(o->expr, "Cannot slice '%s' with non-constant indices", str);
+				gb_string_free(str);
+				o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
+				o->expr = node;
+				return kind;
+			}
+
+			String s = {};
+			if (o->value.kind == ExactValue_String) {
+				s = o->value.value_string;
+			}
+
+			o->mode = Addressing_Constant;
+			o->type = t;
+			o->value = exact_value_string(substring(s, indices[0], indices[1]));
+		}
+
 	case_end;
 
 
@@ -8729,9 +8901,9 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
 
 	if (type != nullptr && is_type_untyped(type)) {
 		add_untyped(&c->checker->info, node, false, o->mode, type, value);
-	} else {
-		add_type_and_value(&c->checker->info, node, o->mode, type, value);
 	}
+	add_type_and_value(&c->checker->info, node, o->mode, type, value);
+
 	return kind;
 }
 

+ 7 - 0
src/ir.cpp

@@ -6978,6 +6978,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 		return ir_addr_load(proc, ir_build_addr(proc, expr));
 	}
 
+	if (tv.mode == Addressing_Constant) {
+		GB_ASSERT(tv.value.kind == ExactValue_Invalid);
+		// NOTE(bill): Zero value constant
+		return ir_add_module_constant(proc->module, tv.type, tv.value);
+	}
+
+
 	switch (expr->kind) {
 	case_ast_node(bl, BasicLit, expr);
 		TokenPos pos = bl->token.pos;

+ 9 - 1
src/types.cpp

@@ -339,7 +339,15 @@ Selection selection_combine(Selection const &lhs, Selection const &rhs) {
 Selection sub_selection(Selection const &sel, isize offset) {
 	Selection res = {};
 	res.index.data = sel.index.data + offset;
-	res.index.count = sel.index.count - offset;
+	res.index.count = gb_max(sel.index.count - offset, 0);
+	res.index.capacity = res.index.count;
+	return res;
+}
+
+Selection sub_selection_with_length(Selection const &sel, isize offset, isize len) {
+	Selection res = {};
+	res.index.data = sel.index.data + offset;
+	res.index.count = gb_max(len, gb_max(sel.index.count - offset, 0));
 	res.index.capacity = res.index.count;
 	return res;
 }