authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-07 10:34:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-07 10:34:54-07:00
log1d4c66b56beb7b2da6bafbab6871691641c8da0b
tree9277fdf3c2b20afe5658dd7915f592a9cca5621e
parent57688dea36e6d1f8d7bad255898d1e222b3a6fbc

support multiple generic function instances

See #22

5 files changed, 318 insertions(+), 67 deletions(-)

src/analyze.cpp+73-40
......@@ -974,7 +974,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
974974 } else {
975975 symbol_name = buf_sprintf("_%s", buf_ptr(&fn_table_entry->symbol_name));
976976 }
977 // TODO mangle the name if it's a generic instance
978977
979978 fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name),
980979 fn_type->data.fn.raw_type_ref);
......@@ -2828,6 +2827,60 @@ static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
28282827 }
28292828}
28302829
2830static bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry) {
2831 switch (type_entry->id) {
2832 case TypeTableEntryIdEnum:
2833 {
2834 ConstEnumValue *enum1 = &a->data.x_enum;
2835 ConstEnumValue *enum2 = &b->data.x_enum;
2836 if (enum1->tag == enum2->tag) {
2837 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum1->tag];
2838 if (type_has_bits(enum_field->type_entry)) {
2839 zig_panic("TODO const expr analyze enum special value for equality");
2840 } else {
2841 return true;
2842 }
2843 }
2844 return false;
2845 }
2846 case TypeTableEntryIdMetaType:
2847 return a->data.x_type == b->data.x_type;
2848 case TypeTableEntryIdVoid:
2849 return true;
2850 case TypeTableEntryIdPureError:
2851 return a->data.x_err.err == b->data.x_err.err;
2852 case TypeTableEntryIdFn:
2853 return a->data.x_fn == b->data.x_fn;
2854 case TypeTableEntryIdBool:
2855 return a->data.x_bool == b->data.x_bool;
2856 case TypeTableEntryIdInt:
2857 case TypeTableEntryIdFloat:
2858 case TypeTableEntryIdNumLitFloat:
2859 case TypeTableEntryIdNumLitInt:
2860 return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum);
2861 case TypeTableEntryIdPointer:
2862 zig_panic("TODO");
2863 case TypeTableEntryIdArray:
2864 zig_panic("TODO");
2865 case TypeTableEntryIdStruct:
2866 zig_panic("TODO");
2867 case TypeTableEntryIdUndefLit:
2868 zig_panic("TODO");
2869 case TypeTableEntryIdMaybe:
2870 zig_panic("TODO");
2871 case TypeTableEntryIdErrorUnion:
2872 zig_panic("TODO");
2873 case TypeTableEntryIdTypeDecl:
2874 zig_panic("TODO");
2875 case TypeTableEntryIdNamespace:
2876 zig_panic("TODO");
2877 case TypeTableEntryIdGenericFn:
2878 case TypeTableEntryIdInvalid:
2879 case TypeTableEntryIdUnreachable:
2880 zig_unreachable();
2881 }
2882}
2883
28312884static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
28322885 AstNode *node)
28332886{
......@@ -2887,39 +2940,8 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
28872940 }
28882941
28892942 answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum);
2890
2891 } else if (resolved_type->id == TypeTableEntryIdEnum) {
2892 ConstEnumValue *enum1 = &op1_val->data.x_enum;
2893 ConstEnumValue *enum2 = &op2_val->data.x_enum;
2894 bool are_equal = false;
2895 if (enum1->tag == enum2->tag) {
2896 TypeEnumField *enum_field = &op1_type->data.enumeration.fields[enum1->tag];
2897 if (type_has_bits(enum_field->type_entry)) {
2898 zig_panic("TODO const expr analyze enum special value for equality");
2899 } else {
2900 are_equal = true;
2901 }
2902 }
2903 if (bin_op_type == BinOpTypeCmpEq) {
2904 answer = are_equal;
2905 } else if (bin_op_type == BinOpTypeCmpNotEq) {
2906 answer = !are_equal;
2907 } else {
2908 zig_unreachable();
2909 }
2910 } else if (resolved_type->id == TypeTableEntryIdPureError) {
2911 bool are_equal = op1_val->data.x_err.err == op2_val->data.x_err.err;
2912
2913 if (bin_op_type == BinOpTypeCmpEq) {
2914 answer = are_equal;
2915 } else if (bin_op_type == BinOpTypeCmpNotEq) {
2916 answer = !are_equal;
2917 } else {
2918 zig_unreachable();
2919 }
2920 } else if (resolved_type->id == TypeTableEntryIdFn) {
2921 bool are_equal = (op1_val->data.x_fn == op2_val->data.x_fn);
2922
2943 } else {
2944 bool are_equal = const_values_equal(op1_val, op2_val, resolved_type);
29232945 if (bin_op_type == BinOpTypeCmpEq) {
29242946 answer = are_equal;
29252947 } else if (bin_op_type == BinOpTypeCmpNotEq) {
......@@ -2927,8 +2949,6 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
29272949 } else {
29282950 zig_unreachable();
29292951 }
2930 } else {
2931 zig_unreachable();
29322952 }
29332953
29342954 bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
......@@ -4682,7 +4702,6 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
46824702 generic_param_value->node = *param_node;
46834703 }
46844704
4685
46864705 auto entry = g->generic_table.maybe_get(generic_fn_type_id);
46874706 if (entry) {
46884707 AstNode *impl_decl_node = entry->value;
......@@ -4693,9 +4712,10 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
46934712
46944713 // make a type from the generic parameters supplied
46954714 assert(decl_node->type == NodeTypeFnProto);
4696 AstNode *impl_fn_def_node = ast_clone_subtree(decl_node->data.fn_proto.fn_def_node);
4715 AstNode *impl_fn_def_node = ast_clone_subtree(decl_node->data.fn_proto.fn_def_node, &g->next_node_index);
46974716 AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto;
4698 impl_decl_node->data.fn_proto.fn_def_node = impl_fn_def_node;
4717
4718
46994719
47004720 preview_fn_proto_instance(g, import, impl_decl_node, child_context);
47014721
......@@ -6233,7 +6253,20 @@ uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) {
62336253}
62346254
62356255bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
6236 // TODO
6256 if (a->decl_node != b->decl_node) return false;
6257 assert(a->generic_param_count == b->generic_param_count);
6258 for (int i = 0; i < a->generic_param_count; i += 1) {
6259 GenericParamValue *a_val = &a->generic_params[i];
6260 GenericParamValue *b_val = &b->generic_params[i];
6261 assert(a_val->type == b_val->type);
6262 ConstExprValue *a_const_val = &get_resolved_expr(a_val->node)->const_val;
6263 ConstExprValue *b_const_val = &get_resolved_expr(b_val->node)->const_val;
6264 assert(a_const_val->ok);
6265 assert(b_const_val->ok);
6266 if (!const_values_equal(a_const_val, b_const_val, a_val->type)) {
6267 return false;
6268 }
6269 }
62376270 return true;
62386271}
62396272
src/parser.cpp+222-11
......@@ -2842,20 +2842,231 @@ void normalize_parent_ptrs(AstNode *node) {
28422842 ast_visit_node_children(node, normalize_parent_ptrs_visit, nullptr);
28432843}
28442844
2845static AstNode *clone_node(AstNode *old_node) {
2846 AstNode *new_node = allocate_nonzero<AstNode>(1);
2847 memcpy(new_node, old_node, sizeof(AstNode));
2848 return new_node;
2845static void clone_subtree_list(ZigList<AstNode *> *dest, ZigList<AstNode *> *src, uint32_t *next_node_index) {
2846 memset(dest, 0, sizeof(ZigList<AstNode *>));
2847 dest->resize(src->length);
2848 for (int i = 0; i < src->length; i += 1) {
2849 dest->at(i) = ast_clone_subtree(src->at(i), next_node_index);
2850 }
28492851}
28502852
2851static void ast_clone_subtree_visit(AstNode **node, void *context) {
2852 *node = clone_node(*node);
2853 (*node)->parent_field = node;
2854 ast_visit_node_children(*node, ast_clone_subtree_visit, nullptr);
2853static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNode *> *src,
2854 uint32_t *next_node_index)
2855{
2856 if (src) {
2857 ZigList<AstNode *> *dest = allocate<ZigList<AstNode *>>(1);
2858 *dest_ptr = dest;
2859 clone_subtree_list(dest, src, next_node_index);
2860 }
2861}
2862
2863static void clone_subtree_field(AstNode **dest, AstNode *src, uint32_t *next_node_index) {
2864 *dest = ast_clone_subtree(src, next_node_index);
2865 (*dest)->parent_field = dest;
2866}
2867
2868static void clone_subtree_tld(TopLevelDecl *dest, TopLevelDecl *src, uint32_t *next_node_index) {
2869 clone_subtree_list_ptr(&dest->directives, src->directives, next_node_index);
28552870}
28562871
2857AstNode *ast_clone_subtree(AstNode *old_node) {
2858 AstNode *new_node = clone_node(old_node);
2859 ast_visit_node_children(new_node, ast_clone_subtree_visit, nullptr);
2872AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) {
2873 AstNode *new_node = allocate_nonzero<AstNode>(1);
2874 memcpy(new_node, old_node, sizeof(AstNode));
2875 new_node->create_index = *next_node_index;
2876 *next_node_index += 1;
2877
2878 switch (new_node->type) {
2879 case NodeTypeRoot:
2880 clone_subtree_list(&new_node->data.root.top_level_decls, &old_node->data.root.top_level_decls,
2881 next_node_index);
2882 break;
2883 case NodeTypeFnProto:
2884 clone_subtree_tld(&new_node->data.fn_proto.top_level_decl, &old_node->data.fn_proto.top_level_decl,
2885 next_node_index);
2886 clone_subtree_field(&new_node->data.fn_proto.return_type, old_node->data.fn_proto.return_type,
2887 next_node_index);
2888 clone_subtree_list(&new_node->data.fn_proto.generic_params,
2889 &old_node->data.fn_proto.generic_params, next_node_index);
2890 clone_subtree_list(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params,
2891 next_node_index);
2892
2893 break;
2894 case NodeTypeFnDef:
2895 clone_subtree_field(&new_node->data.fn_def.fn_proto, old_node->data.fn_def.fn_proto, next_node_index);
2896 new_node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = new_node;
2897 clone_subtree_field(&new_node->data.fn_def.body, old_node->data.fn_def.body, next_node_index);
2898 break;
2899 case NodeTypeFnDecl:
2900 clone_subtree_field(&new_node->data.fn_decl.fn_proto, old_node->data.fn_decl.fn_proto,
2901 next_node_index);
2902 break;
2903 case NodeTypeParamDecl:
2904 clone_subtree_field(&new_node->data.param_decl.type, old_node->data.param_decl.type, next_node_index);
2905 break;
2906 case NodeTypeBlock:
2907 clone_subtree_list(&new_node->data.block.statements, &old_node->data.block.statements,
2908 next_node_index);
2909 break;
2910 case NodeTypeDirective:
2911 clone_subtree_field(&new_node->data.directive.expr, old_node->data.directive.expr, next_node_index);
2912 break;
2913 case NodeTypeReturnExpr:
2914 clone_subtree_field(&new_node->data.return_expr.expr, old_node->data.return_expr.expr, next_node_index);
2915 break;
2916 case NodeTypeDefer:
2917 clone_subtree_field(&new_node->data.defer.expr, old_node->data.defer.expr, next_node_index);
2918 break;
2919 case NodeTypeVariableDeclaration:
2920 clone_subtree_list_ptr(&new_node->data.variable_declaration.top_level_decl.directives,
2921 old_node->data.variable_declaration.top_level_decl.directives, next_node_index);
2922 clone_subtree_field(&new_node->data.variable_declaration.type, old_node->data.variable_declaration.type, next_node_index);
2923 clone_subtree_field(&new_node->data.variable_declaration.expr, old_node->data.variable_declaration.expr, next_node_index);
2924 break;
2925 case NodeTypeTypeDecl:
2926 clone_subtree_list_ptr(&new_node->data.type_decl.top_level_decl.directives,
2927 old_node->data.type_decl.top_level_decl.directives, next_node_index);
2928 clone_subtree_field(&new_node->data.type_decl.child_type, old_node->data.type_decl.child_type, next_node_index);
2929 break;
2930 case NodeTypeErrorValueDecl:
2931 // none
2932 break;
2933 case NodeTypeBinOpExpr:
2934 clone_subtree_field(&new_node->data.bin_op_expr.op1, old_node->data.bin_op_expr.op1, next_node_index);
2935 clone_subtree_field(&new_node->data.bin_op_expr.op2, old_node->data.bin_op_expr.op2, next_node_index);
2936 break;
2937 case NodeTypeUnwrapErrorExpr:
2938 clone_subtree_field(&new_node->data.unwrap_err_expr.op1, old_node->data.unwrap_err_expr.op1, next_node_index);
2939 clone_subtree_field(&new_node->data.unwrap_err_expr.symbol, old_node->data.unwrap_err_expr.symbol, next_node_index);
2940 clone_subtree_field(&new_node->data.unwrap_err_expr.op2, old_node->data.unwrap_err_expr.op2, next_node_index);
2941 break;
2942 case NodeTypeNumberLiteral:
2943 // none
2944 break;
2945 case NodeTypeStringLiteral:
2946 // none
2947 break;
2948 case NodeTypeCharLiteral:
2949 // none
2950 break;
2951 case NodeTypeSymbol:
2952 // none
2953 break;
2954 case NodeTypePrefixOpExpr:
2955 clone_subtree_field(&new_node->data.prefix_op_expr.primary_expr, old_node->data.prefix_op_expr.primary_expr, next_node_index);
2956 break;
2957 case NodeTypeFnCallExpr:
2958 clone_subtree_field(&new_node->data.fn_call_expr.fn_ref_expr, old_node->data.fn_call_expr.fn_ref_expr, next_node_index);
2959 clone_subtree_list(&new_node->data.fn_call_expr.params, &old_node->data.fn_call_expr.params, next_node_index);
2960 break;
2961 case NodeTypeArrayAccessExpr:
2962 clone_subtree_field(&new_node->data.array_access_expr.array_ref_expr, old_node->data.array_access_expr.array_ref_expr, next_node_index);
2963 clone_subtree_field(&new_node->data.array_access_expr.subscript, old_node->data.array_access_expr.subscript, next_node_index);
2964 break;
2965 case NodeTypeSliceExpr:
2966 clone_subtree_field(&new_node->data.slice_expr.array_ref_expr, old_node->data.slice_expr.array_ref_expr, next_node_index);
2967 clone_subtree_field(&new_node->data.slice_expr.start, old_node->data.slice_expr.start, next_node_index);
2968 clone_subtree_field(&new_node->data.slice_expr.end, old_node->data.slice_expr.end, next_node_index);
2969 break;
2970 case NodeTypeFieldAccessExpr:
2971 clone_subtree_field(&new_node->data.field_access_expr.struct_expr, old_node->data.field_access_expr.struct_expr, next_node_index);
2972 break;
2973 case NodeTypeUse:
2974 clone_subtree_field(&new_node->data.use.expr, old_node->data.use.expr, next_node_index);
2975 clone_subtree_list_ptr(&new_node->data.use.top_level_decl.directives,
2976 old_node->data.use.top_level_decl.directives, next_node_index);
2977 break;
2978 case NodeTypeBoolLiteral:
2979 // none
2980 break;
2981 case NodeTypeNullLiteral:
2982 // none
2983 break;
2984 case NodeTypeUndefinedLiteral:
2985 // none
2986 break;
2987 case NodeTypeIfBoolExpr:
2988 clone_subtree_field(&new_node->data.if_bool_expr.condition, old_node->data.if_bool_expr.condition, next_node_index);
2989 clone_subtree_field(&new_node->data.if_bool_expr.then_block, old_node->data.if_bool_expr.then_block, next_node_index);
2990 clone_subtree_field(&new_node->data.if_bool_expr.else_node, old_node->data.if_bool_expr.else_node, next_node_index);
2991 break;
2992 case NodeTypeIfVarExpr:
2993 clone_subtree_field(&new_node->data.if_var_expr.var_decl.type, old_node->data.if_var_expr.var_decl.type, next_node_index);
2994 clone_subtree_field(&new_node->data.if_var_expr.var_decl.expr, old_node->data.if_var_expr.var_decl.expr, next_node_index);
2995 clone_subtree_field(&new_node->data.if_var_expr.then_block, old_node->data.if_var_expr.then_block, next_node_index);
2996 clone_subtree_field(&new_node->data.if_var_expr.else_node, old_node->data.if_var_expr.else_node, next_node_index);
2997 break;
2998 case NodeTypeWhileExpr:
2999 clone_subtree_field(&new_node->data.while_expr.condition, old_node->data.while_expr.condition, next_node_index);
3000 clone_subtree_field(&new_node->data.while_expr.body, old_node->data.while_expr.body, next_node_index);
3001 break;
3002 case NodeTypeForExpr:
3003 clone_subtree_field(&new_node->data.for_expr.elem_node, old_node->data.for_expr.elem_node, next_node_index);
3004 clone_subtree_field(&new_node->data.for_expr.array_expr, old_node->data.for_expr.array_expr, next_node_index);
3005 clone_subtree_field(&new_node->data.for_expr.index_node, old_node->data.for_expr.index_node, next_node_index);
3006 clone_subtree_field(&new_node->data.for_expr.body, old_node->data.for_expr.body, next_node_index);
3007 break;
3008 case NodeTypeSwitchExpr:
3009 clone_subtree_field(&new_node->data.switch_expr.expr, old_node->data.switch_expr.expr, next_node_index);
3010 clone_subtree_list(&new_node->data.switch_expr.prongs, &old_node->data.switch_expr.prongs,
3011 next_node_index);
3012 break;
3013 case NodeTypeSwitchProng:
3014 clone_subtree_list(&new_node->data.switch_prong.items, &old_node->data.switch_prong.items,
3015 next_node_index);
3016 clone_subtree_field(&new_node->data.switch_prong.var_symbol, old_node->data.switch_prong.var_symbol, next_node_index);
3017 clone_subtree_field(&new_node->data.switch_prong.expr, old_node->data.switch_prong.expr, next_node_index);
3018 break;
3019 case NodeTypeSwitchRange:
3020 clone_subtree_field(&new_node->data.switch_range.start, old_node->data.switch_range.start, next_node_index);
3021 clone_subtree_field(&new_node->data.switch_range.end, old_node->data.switch_range.end, next_node_index);
3022 break;
3023 case NodeTypeLabel:
3024 // none
3025 break;
3026 case NodeTypeGoto:
3027 // none
3028 break;
3029 case NodeTypeBreak:
3030 // none
3031 break;
3032 case NodeTypeContinue:
3033 // none
3034 break;
3035 case NodeTypeAsmExpr:
3036 zig_panic("TODO");
3037 break;
3038 case NodeTypeStructDecl:
3039 clone_subtree_list(&new_node->data.struct_decl.fields, &old_node->data.struct_decl.fields,
3040 next_node_index);
3041 clone_subtree_list(&new_node->data.struct_decl.fns, &old_node->data.struct_decl.fns,
3042 next_node_index);
3043 clone_subtree_list_ptr(&new_node->data.struct_decl.top_level_decl.directives,
3044 old_node->data.struct_decl.top_level_decl.directives, next_node_index);
3045 break;
3046 case NodeTypeStructField:
3047 clone_subtree_field(&new_node->data.struct_field.type, old_node->data.struct_field.type, next_node_index);
3048 clone_subtree_list_ptr(&new_node->data.struct_field.top_level_decl.directives,
3049 old_node->data.struct_field.top_level_decl.directives, next_node_index);
3050 break;
3051 case NodeTypeContainerInitExpr:
3052 clone_subtree_field(&new_node->data.container_init_expr.type, old_node->data.container_init_expr.type, next_node_index);
3053 clone_subtree_list(&new_node->data.container_init_expr.entries,
3054 &old_node->data.container_init_expr.entries, next_node_index);
3055 break;
3056 case NodeTypeStructValueField:
3057 clone_subtree_field(&new_node->data.struct_val_field.expr, old_node->data.struct_val_field.expr, next_node_index);
3058 break;
3059 case NodeTypeArrayType:
3060 clone_subtree_field(&new_node->data.array_type.size, old_node->data.array_type.size, next_node_index);
3061 clone_subtree_field(&new_node->data.array_type.child_type, old_node->data.array_type.child_type, next_node_index);
3062 break;
3063 case NodeTypeErrorType:
3064 // none
3065 break;
3066 case NodeTypeTypeLiteral:
3067 // none
3068 break;
3069 }
3070
28603071 return new_node;
28613072}
src/parser.hpp+1-1
......@@ -24,7 +24,7 @@ void ast_print(AstNode *node, int indent);
2424
2525void normalize_parent_ptrs(AstNode *node);
2626
27AstNode *ast_clone_subtree(AstNode *node);
27AstNode *ast_clone_subtree(AstNode *node, uint32_t *next_node_index);
2828void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context);
2929
3030#endif
std/rand.zig+16-15
......@@ -1,11 +1,26 @@
11// Mersenne Twister
22const ARRAY_SIZE = 624;
33
4/// Use `rand_init` to initialize this state.
4/// Use `init` to initialize this state.
55pub struct Rand {
66 array: [ARRAY_SIZE]u32,
77 index: isize,
88
9 /// Initialize random state with the given seed.
10 pub fn init(seed: u32) -> Rand {
11 var r: Rand = undefined;
12 r.index = 0;
13 r.array[0] = seed;
14 var i : isize = 1;
15 var prev_value: u64 = seed;
16 while (i < ARRAY_SIZE) {
17 r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u32(i));
18 prev_value = r.array[i];
19 i += 1;
20 }
21 return r;
22 }
23
924 /// Get 32 bits of randomness.
1025 pub fn get_u32(r: &Rand) -> u32 {
1126 if (r.index == 0) {
......@@ -85,20 +100,6 @@ pub struct Rand {
85100 return bytes_left;
86101 }
87102
88 /// Initialize random state with the given seed.
89 pub fn init(seed: u32) -> Rand {
90 var r: Rand = undefined;
91 r.index = 0;
92 r.array[0] = seed;
93 var i : isize = 1;
94 var prev_value: u64 = seed;
95 while (i < ARRAY_SIZE) {
96 r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u32(i));
97 prev_value = r.array[i];
98 i += 1;
99 }
100 return r;
101 }
102103}
103104
104105#attribute("test")
test/self_hosted.zig+6
......@@ -515,6 +515,7 @@ three)";
515515#attribute("test")
516516fn simple_generic_fn() {
517517 assert(max(i32)(3, -1) == 3);
518 assert(max(f32)(0.123, 0.456) == 0.456);
518519}
519520
520521fn max(T: type)(a: T, b: T) -> T {
......@@ -530,6 +531,11 @@ fn constant_equal_function_pointers() {
530531
531532fn empty_fn() {}
532533
534#attribute("test")
535fn generic_function_equality() {
536 assert(max(i32) == max(i32));
537}
538
533539
534540fn assert(b: bool) {
535541 if (!b) unreachable{}