Almost have scoped . working, in fact it is, but having objects with member names has problems (like o.o) if the member function is in scope. (it thinks maybe you're trying to call o on itself...)

This commit is contained in:
Nathan Braswell
2018-12-18 02:51:44 -05:00
parent eadadd5576
commit 66f82062ba
4 changed files with 109 additions and 47 deletions

138
k.krak
View File

@@ -280,7 +280,7 @@ fun main(argc: int, argv: **char): int {
return inst_temp_type(get_type(a->children[0]), b.second.associate(fun(k: str, v: *binding<type>): pair<*binding<type>, *binding<type>> return make_pair(v, binding_p(type::_unknown()));)) return inst_temp_type(get_type(a->children[0]), b.second.associate(fun(k: str, v: *binding<type>): pair<*binding<type>, *binding<type>> return make_pair(v, binding_p(type::_unknown()));))
} }
ast::_compiler_intrinsic(b) return b.second ast::_compiler_intrinsic(b) return b.second
ast::_call() { ast::_call(add_scope) {
var t = get_type(a->children[0]) var t = get_type(a->children[0])
if (is_fun(t->bound_to)) if (is_fun(t->bound_to))
return t->bound_to->_fun.first.second return t->bound_to->_fun.first.second
@@ -365,33 +365,57 @@ fun main(argc: int, argv: **char): int {
to_ret += primitive_ops[name] to_ret += primitive_ops[name]
return OptionVecAst::Some(to_ret) return OptionVecAst::Some(to_ret)
} }
var try_to_find_binding_possibilities = fun(binding: *tree<ast>, start_scope: *tree<ast>, type_binding: bool) { var try_to_find_binding_possibilities = fun(binding: *tree<ast>, start_scope: *tree<ast>, additional_scope: *tree<ast>, type_binding: bool): bool {
if !ast_bound(binding) && !multiple_binding_options.contains_key(binding) { if !ast_bound(binding) && !multiple_binding_options.contains_key(binding) {
var all_options = vec<*tree<ast>>()
match (scope_lookup(start_scope, ast_binding_str(binding), type_binding)) { match (scope_lookup(start_scope, ast_binding_str(binding), type_binding)) {
OptionVecAst::None() { OptionVecAst::None() {
println("OptionVecAst::None for " + ast_binding_str(binding) + " lookup, returning") println("OptionVecAst::None for " + ast_binding_str(binding) + " lookup, returning")
return; return false;
} }
OptionVecAst::Some(options) { OptionVecAst::Some(options) {
println("OptionVecAst::Some for " + ast_binding_str(binding) + " lookup, continuing!") println("OptionVecAst::Some for " + ast_binding_str(binding) + " lookup, continuing!")
if (options.size == 0) all_options += options
}
}
if additional_scope != null<tree<ast>>() {
println("Additionally looking at scope " + to_string(additional_scope->data) + " for try_to_find_binding_possibilities " + ast_binding_str(binding))
match (scope_lookup(additional_scope, ast_binding_str(binding), type_binding)) {
OptionVecAst::None() {
println("OptionVecAst::None for " + ast_binding_str(binding) + " lookup, returning")
return false;
}
OptionVecAst::Some(options) {
println("OptionVecAst::Some for " + ast_binding_str(binding) + " lookup, continuing!")
all_options.add_all_unique(options)
}
}
}
if (all_options.size == 0) {
error("Could not find any options for scope lookup of " + ast_binding_str(binding)) error("Could not find any options for scope lookup of " + ast_binding_str(binding))
else if (options.size == 1) } else if (all_options.size == 1) {
set_ast_binding(binding, options[0]) println(ast_binding_str(binding) + " resolved to a single!")
else set_ast_binding(binding, all_options[0])
multiple_binding_options[binding] = options } else {
println(ast_binding_str(binding) + " found to have " + all_options.size + " options!")
for (var i = 0; i < all_options.size; i++;)
println("\t" + to_string(all_options[i]->data))
multiple_binding_options[binding] = all_options
}
return true;
} }
} }
} var handle_type_binding_possibilities: fun(*binding<type>, *tree<ast>): bool = fun(t: *binding<type>, n: *tree<ast>): bool {
}
var handle_type_binding_possibilities: fun(*binding<type>, *tree<ast>): void = fun(t: *binding<type>, n: *tree<ast>) {
match(*t->bound_to) { match(*t->bound_to) {
type::_obj(b) try_to_find_binding_possibilities(b, n, true) type::_obj(b) return try_to_find_binding_possibilities(b, n, null<tree<ast>>(), true)
type::_fun(b) { type::_fun(b) {
b.first.first.for_each(fun(it: *binding<type>) { /*b.first.first.for_each(fun(it: *binding<type>) {*/
handle_type_binding_possibilities(it, n) /*handle_type_binding_possibilities(it, n)*/
}) /*})*/
handle_type_binding_possibilities(b.first.second, n) for (var i = 0; i < b.first.first.size; i++;)
if (!handle_type_binding_possibilities(b.first.first[i], n))
return false
return handle_type_binding_possibilities(b.first.second, n)
} }
} }
} }
@@ -401,7 +425,7 @@ fun main(argc: int, argv: **char): int {
match (t->data) { match (t->data) {
ast::_declaration() if (t->children.size > 1) ast::_declaration() if (t->children.size > 1)
unify(get_type(t->children[0]), get_type(t->children[1])) unify(get_type(t->children[0]), get_type(t->children[1]))
ast::_call() { ast::_call(add_scope) {
println("traverse_for_unify call - " + to_string(t->data)) println("traverse_for_unify call - " + to_string(t->data))
// we call get type to make sure if it is unknown it is transformed into a function version // we call get type to make sure if it is unknown it is transformed into a function version
get_type(t) get_type(t)
@@ -424,40 +448,72 @@ fun main(argc: int, argv: **char): int {
var more_to_do = true var more_to_do = true
while (more_to_do) { while (more_to_do) {
println("RESOLVE LOOP BEGIN")
more_to_do = false more_to_do = false
var work_done = false var work_done = false
var traverse_for_select: fun(*tree<ast>): bool = fun(t: *tree<ast>): bool { var traverse_for_select: fun(*tree<ast>): bool = fun(t: *tree<ast>): bool {
var children_start_index = 0
match (t->data) { match (t->data) {
ast::_identifier(b) if (!handle_type_binding_possibilities(b.second, t))
ast::_identifier(b) handle_type_binding_possibilities(b.second, t) return false;
/*_binding: triple<str, vec<*type>, *tree<ast>>,*/ /*_binding: triple<str, vec<*type>, *tree<ast>>,*/
ast::_function(b) handle_type_binding_possibilities(b.second, t) ast::_function(b) if (!handle_type_binding_possibilities(b.second, t))
return false;
ast::_compiler_intrinsic(b) { ast::_compiler_intrinsic(b) {
handle_type_binding_possibilities(b.second, t) if (!handle_type_binding_possibilities(b.second, t))
b.third.for_each(fun(tb: *binding<type>) { return false;
handle_type_binding_possibilities(tb, t) for (var i = 0; i < b.third.size; i++;)
}) if (!handle_type_binding_possibilities(b.third[i], t))
return false;
/*b.third.for_each(fun(tb: *binding<type>) {*/
/*handle_type_binding_possibilities(tb, t)*/
/*})*/
}
ast::_cast(b) if (!handle_type_binding_possibilities(b, t))
return false
ast::_call(add_scope) {
println("call of " + to_string(t->children[0]) + ", that is " + to_string(t->children[0]->data) + " has type " + to_string(get_type(t)->bound_to) + ", and the function has type " + to_string(get_type(t->children[0])->bound_to))
/*get_type(t)*/
if add_scope && is_binding(t->children[0]) && !ast_bound(t->children[0]) && !multiple_binding_options.contains_key(t->children[0]) {
var first_param_type = get_type(t->children[1])
if !is_unknown(first_param_type->bound_to) && (!is_obj(first_param_type->bound_to) || ast_bound(first_param_type->bound_to->_obj)) {
if is_obj(first_param_type->bound_to) {
if (!try_to_find_binding_possibilities(t->children[0], t->children[0], get_ast_binding(first_param_type->bound_to->_obj)->parent, false))
return false;
} else {
if (!try_to_find_binding_possibilities(t->children[0], t->children[0], null<tree<ast>>(), false))
return false
}
work_done = true
println("wok done! generic add posibilities (or down to one) for " + to_string(t->children[0]->data))
if ast_bound(t->children[0]) {
unify(binding_types[t->children[0]], get_type(get_ast_binding(t->children[0])))
}
} else {
children_start_index = 1
more_to_do = true
}
}
} }
ast::_cast(b) handle_type_binding_possibilities(b, t)
ast::_binding(b) if (!ast_bound(t)) { ast::_binding(b) if (!ast_bound(t)) {
println(to_string(t->data) + " - not bound!") println(to_string(t->data) + " - not bound!")
if !multiple_binding_options.contains_key(t) { if !multiple_binding_options.contains_key(t) {
try_to_find_binding_possibilities(t, t, false) if (!try_to_find_binding_possibilities(t, t, null<tree<ast>>(), false))
return false
} }
if ast_bound(t) { if ast_bound(t) {
unify(binding_types[t], get_type(get_ast_binding(t))) unify(binding_types[t], get_type(get_ast_binding(t)))
work_done = true
println("wok done! set " + to_string(t->data))
} else { } else {
if !multiple_binding_options.contains_key(t) {
println(" must have added a pass! going around the horn again... before")
return false;
}
var filtered_options = multiple_binding_options[t].filter(fun(p: *tree<ast>): bool return equality(binding_types[t]->bound_to, get_type(p)->bound_to, true);) var filtered_options = multiple_binding_options[t].filter(fun(p: *tree<ast>): bool return equality(binding_types[t]->bound_to, get_type(p)->bound_to, true);)
if (filtered_options.size == 0) { if (filtered_options.size == 0) {
println("Attempting to use our inferenced type " + to_string(binding_types[t]->bound_to) + " to decide what to bind " + to_string(t->data) + " to from options:") println("Attempting to use our inferenced type " + to_string(binding_types[t]->bound_to) + " to decide what to bind " + to_string(t->data) + " to from options:")
multiple_binding_options[t].for_each(fun(p: *tree<ast>) { println("\t" + to_string(p->data) + " of type " + to_string(get_type(p)->bound_to)); }) multiple_binding_options[t].for_each(fun(p: *tree<ast>) { println("\t" + to_string(p->data) + " of type " + to_string(get_type(p)->bound_to)); })
error("no options remain after filtering overloads by type for " + to_string(t->data)) error("no options remain after filtering overloads by type for " + to_string(t->data))
} else if (filtered_options.size > 1) { } else if (filtered_options.size > 1) {
println("inferenced type " + to_string(binding_types[t]->bound_to) + " HAD MULTIPLE OPTIONS AFTER FILTER for " + to_string(t) + ", that is "+ to_string(t->data))
more_to_do = true more_to_do = true
} else { } else {
set_ast_binding(t, filtered_options[0]) set_ast_binding(t, filtered_options[0])
@@ -468,7 +524,7 @@ fun main(argc: int, argv: **char): int {
} }
} }
} }
for (var i = 0; i < t->children.size; i++;) { for (var i = children_start_index; i < t->children.size; i++;) {
if !traverse_for_select(t->children[i]) { if !traverse_for_select(t->children[i]) {
return false return false
} }
@@ -483,6 +539,7 @@ fun main(argc: int, argv: **char): int {
var traverse_for_error: fun(*tree<ast>): void = fun(t: *tree<ast>) { var traverse_for_error: fun(*tree<ast>): void = fun(t: *tree<ast>) {
match (t->data) { match (t->data) {
ast::_binding(b) if (!ast_bound(t)) { ast::_binding(b) if (!ast_bound(t)) {
println("Trying to error out because we made no progress")
var filtered_options = multiple_binding_options[t].filter(fun(p: *tree<ast>): bool return equality(binding_types[t]->bound_to, get_type(p)->bound_to, true);) var filtered_options = multiple_binding_options[t].filter(fun(p: *tree<ast>): bool return equality(binding_types[t]->bound_to, get_type(p)->bound_to, true);)
if (filtered_options.size > 1) { if (filtered_options.size > 1) {
println("Attempting to use our inferenced type " + to_string(binding_types[t]->bound_to) + " to decide what to bind " + to_string(t->data) + " to form options:") println("Attempting to use our inferenced type " + to_string(binding_types[t]->bound_to) + " to decide what to bind " + to_string(t->data) + " to form options:")
@@ -883,7 +940,7 @@ fun main(argc: int, argv: **char): int {
ast::_break() { C_str += idt + "break"; } ast::_break() { C_str += idt + "break"; }
ast::_continue() { C_str += idt + "continue"; } ast::_continue() { C_str += idt + "continue"; }
ast::_defer() { error("no defer should remain at C emit"); } ast::_defer() { error("no defer should remain at C emit"); }
ast::_call() { ast::_call(add_scope) {
if (is_compiler_intrinsic(get_ast_binding(t->children[0]))) { if (is_compiler_intrinsic(get_ast_binding(t->children[0]))) {
if (t->children.size == 2) { if (t->children.size == 2) {
var intrinsic_name = get_ast_binding(t->children[0])->data._compiler_intrinsic.first var intrinsic_name = get_ast_binding(t->children[0])->data._compiler_intrinsic.first
@@ -940,7 +997,7 @@ fun main(argc: int, argv: **char): int {
true, vec( true, vec(
_identifier(str("argc"), binding_p(type::_int())), _identifier(str("argc"), binding_p(type::_int())),
_identifier(str("argv"), binding_p(type::_ptr(binding_p(type::_ptr(binding_p(type::_char())))))), _identifier(str("argv"), binding_p(type::_ptr(binding_p(type::_ptr(binding_p(type::_char())))))),
_return(vec(_call(vec(make_ast_binding("fmain"), make_ast_binding("argc"), make_ast_binding("argv"))))) _return(vec(_call(false, vec(make_ast_binding("fmain"), make_ast_binding("argc"), make_ast_binding("argv")))))
) )
) )
var top_unit = _translation_unit(str(), vec( var top_unit = _translation_unit(str(), vec(
@@ -1175,27 +1232,28 @@ fun syntax_to_ast(file_name: str, syntax: *tree<symbol>, import_paths: ref vec<s
children += get_nodes("boolean_expression", syntax).map(fun(x: *tree<symbol>): *tree<ast> return syntax_to_ast_helper(x, declared_template_types);) children += get_nodes("boolean_expression", syntax).map(fun(x: *tree<symbol>): *tree<ast> return syntax_to_ast_helper(x, declared_template_types);)
return _declaration(children) return _declaration(children)
} else if (syntax->data.name == "assignment_statement") { } else if (syntax->data.name == "assignment_statement") {
return _call(vec(make_ast_binding("op" + concat(syntax->children[1])), return _call(false, vec(make_ast_binding("op" + concat(syntax->children[1])),
syntax_to_ast_helper(syntax->children[0], declared_template_types), syntax_to_ast_helper(syntax->children[0], declared_template_types),
syntax_to_ast_helper(syntax->children[2], declared_template_types))) syntax_to_ast_helper(syntax->children[2], declared_template_types)))
} else if (syntax->data.name == "function_call") { } else if (syntax->data.name == "function_call") {
// if method, pull out // if method, pull out
if syntax->children[0]->data.name == "unarad" && syntax->children[0]->children[0]->data.name == "access_operation" { if syntax->children[0]->data.name == "unarad" && syntax->children[0]->children[0]->data.name == "access_operation" {
println("doing a method call!") println("doing a method call!")
return _call(vec(syntax_to_ast_helper(syntax->children[0]->children[0]->children[2], declared_template_types)) + syntax_to_ast_helper(syntax->children[0]->children[0]->children[0], declared_template_types) + get_nodes("parameter", syntax).map(fun(s: *tree<symbol>): *tree<ast> { /*return _call(vec(syntax_to_ast_helper(syntax->children[0]->children[0]->children[2], declared_template_types)) + syntax_to_ast_helper(syntax->children[0]->children[0]->children[0], declared_template_types) + get_nodes("parameter", syntax).map(fun(s: *tree<symbol>): *tree<ast> {*/
return _call(true, vec(make_ast_binding(concat(syntax->children[0]->children[0]->children[2]))) + syntax_to_ast_helper(syntax->children[0]->children[0]->children[0], declared_template_types) + get_nodes("parameter", syntax).map(fun(s: *tree<symbol>): *tree<ast> {
return syntax_to_ast_helper(s->children[0], declared_template_types) return syntax_to_ast_helper(s->children[0], declared_template_types)
})) }))
} else { } else {
println("NOT doing a method call! - is " + syntax->children[0]->data.name + " not unrad, or") println("NOT doing a method call! - is " + syntax->children[0]->data.name + " not unrad, or")
println(syntax->children[0]->children[0]->data.name + " not access_operation") println(syntax->children[0]->children[0]->data.name + " not access_operation")
return _call(vec(syntax_to_ast_helper(syntax->children[0], declared_template_types)) + get_nodes("parameter", syntax).map(fun(s: *tree<symbol>): *tree<ast> { return _call(false, vec(syntax_to_ast_helper(syntax->children[0], declared_template_types)) + get_nodes("parameter", syntax).map(fun(s: *tree<symbol>): *tree<ast> {
return syntax_to_ast_helper(s->children[0], declared_template_types) return syntax_to_ast_helper(s->children[0], declared_template_types)
})) }))
} }
} else if (syntax->data.name == "access_operation") { } else if (syntax->data.name == "access_operation") {
// somehow note / do the crazier scope lookup // somehow note / do the crazier scope lookup
// also handle . vs -> // also handle . vs ->
return _call(vec(make_ast_binding(concat(syntax->children[2])), syntax_to_ast_helper(syntax->children[0], declared_template_types))) return _call(true, vec(make_ast_binding(concat(syntax->children[2])), syntax_to_ast_helper(syntax->children[0], declared_template_types)))
} else if (syntax->data.name == "boolean_expression" || } else if (syntax->data.name == "boolean_expression" ||
syntax->data.name == "and_boolean_expression" || syntax->data.name == "and_boolean_expression" ||
syntax->data.name == "bitwise_or" || syntax->data.name == "bitwise_or" ||
@@ -1218,14 +1276,14 @@ fun syntax_to_ast(file_name: str, syntax: *tree<symbol>, import_paths: ref vec<s
return parse_type(s, declared_template_types); return parse_type(s, declared_template_types);
})) }))
} else if (syntax->children[0]->data.terminal) { } else if (syntax->children[0]->data.terminal) {
return _call(vec(make_ast_binding("op" + concat(syntax->children[0])), return _call(true, vec(make_ast_binding("op" + concat(syntax->children[0])),
syntax_to_ast_helper(syntax->children[1], declared_template_types))) syntax_to_ast_helper(syntax->children[1], declared_template_types)))
} else { } else {
return _call(vec(make_ast_binding("op" + concat(syntax->children[1])), return _call(true, vec(make_ast_binding("op" + concat(syntax->children[1])),
syntax_to_ast_helper(syntax->children[0], declared_template_types))) syntax_to_ast_helper(syntax->children[0], declared_template_types)))
} }
} else { } else {
return _call(vec(make_ast_binding("op" + concat(syntax->children[1])), return _call(true, vec(make_ast_binding("op" + concat(syntax->children[1])),
syntax_to_ast_helper(syntax->children[0], declared_template_types), syntax_to_ast_helper(syntax->children[0], declared_template_types),
syntax_to_ast_helper(syntax->children[2], declared_template_types))) syntax_to_ast_helper(syntax->children[2], declared_template_types)))
} }

View File

@@ -28,7 +28,7 @@ adt ast {
_break, _break,
_continue, _continue,
_defer, _defer,
_call, _call: bool,
_compiler_intrinsic: triple<str, *binding<type>, vec<*binding<type>>>, _compiler_intrinsic: triple<str, *binding<type>, vec<*binding<type>>>,
_cast: *binding<type>, _cast: *binding<type>,
_value: pair<str, *binding<type>> _value: pair<str, *binding<type>>
@@ -64,7 +64,7 @@ fun to_string(a: ref ast): str {
ast::_break() return str("_break") ast::_break() return str("_break")
ast::_continue() return str("_continue") ast::_continue() return str("_continue")
ast::_defer() return str("_defer") ast::_defer() return str("_defer")
ast::_call() return str("_call") ast::_call(b) return "_call(add_scope: " + to_string(b) + ")"
ast::_compiler_intrinsic(b) return str("_compiler_intrinsic(") + b.first + ": " + deref_to_string(b.second->bound_to) + ")" ast::_compiler_intrinsic(b) return str("_compiler_intrinsic(") + b.first + ": " + deref_to_string(b.second->bound_to) + ")"
ast::_cast(b) return str("_cast") ast::_cast(b) return str("_cast")
ast::_value(b) return str("_value(") + b.first + ": " + deref_to_string(b.second->bound_to) + ")" ast::_value(b) return str("_value(") + b.first + ": " + deref_to_string(b.second->bound_to) + ")"
@@ -136,8 +136,8 @@ fun _continue(): *tree<ast> {
fun _defer(): *tree<ast> { fun _defer(): *tree<ast> {
return new<tree<ast>>()->construct(ast::_defer()) return new<tree<ast>>()->construct(ast::_defer())
} }
fun _call(): *tree<ast> { fun _call(add_scope: bool): *tree<ast> {
return new<tree<ast>>()->construct(ast::_call()) return new<tree<ast>>()->construct(ast::_call(add_scope))
} }
@@ -202,8 +202,8 @@ fun _return(c: ref vec<*tree<ast>>): *tree<ast> {
fun _defer(c: ref vec<*tree<ast>>): *tree<ast> { fun _defer(c: ref vec<*tree<ast>>): *tree<ast> {
return new<tree<ast>>()->construct(ast::_defer(), c) return new<tree<ast>>()->construct(ast::_defer(), c)
} }
fun _call(c: ref vec<*tree<ast>>): *tree<ast> { fun _call(add_scope: bool, c: ref vec<*tree<ast>>): *tree<ast> {
return new<tree<ast>>()->construct(ast::_call(), c) return new<tree<ast>>()->construct(ast::_call(add_scope), c)
} }
@@ -227,7 +227,7 @@ fun is_return(i: *tree<ast>): bool { match(i->data) { ast::_return() return true
fun is_break(i: *tree<ast>): bool { match(i->data) { ast::_break() return true; } return false; } fun is_break(i: *tree<ast>): bool { match(i->data) { ast::_break() return true; } return false; }
fun is_continue(i: *tree<ast>): bool { match(i->data) { ast::_continue() return true; } return false; } fun is_continue(i: *tree<ast>): bool { match(i->data) { ast::_continue() return true; } return false; }
fun is_defer(i: *tree<ast>): bool { match(i->data) { ast::_defer() return true; } return false; } fun is_defer(i: *tree<ast>): bool { match(i->data) { ast::_defer() return true; } return false; }
fun is_call(i: *tree<ast>): bool { match(i->data) { ast::_call() return true; } return false; } fun is_call(i: *tree<ast>): bool { match(i->data) { ast::_call(b) return true; } return false; }
fun is_compiler_intrinsic(i: *tree<ast>): bool { match(i->data) { ast::_compiler_intrinsic(b) return true; } return false; } fun is_compiler_intrinsic(i: *tree<ast>): bool { match(i->data) { ast::_compiler_intrinsic(b) return true; } return false; }
fun is_cast(i: *tree<ast>): bool { match(i->data) { ast::_cast(b) return true; } return false; } fun is_cast(i: *tree<ast>): bool { match(i->data) { ast::_cast(b) return true; } return false; }
fun is_value(i: *tree<ast>): bool { match(i->data) { ast::_value(b) return true; } return false; } fun is_value(i: *tree<ast>): bool { match(i->data) { ast::_value(b) return true; } return false; }

View File

@@ -111,7 +111,7 @@ fun inst_temp_type(t: *binding<type>, replacements: ref map<*binding<type>, *bin
} }
fun equality(a: *type, b: *type, count_unknown_as_equal: bool): bool { fun equality(a: *type, b: *type, count_unknown_as_equal: bool): bool {
println("equality of " + to_string(a) + " and " + to_string(b)) /*println("equality of " + to_string(a) + " and " + to_string(b))*/
if (count_unknown_as_equal && (is_unknown(a) || is_unknown(b))) if (count_unknown_as_equal && (is_unknown(a) || is_unknown(b)))
return true return true
match(*a) { match(*a) {

View File

@@ -217,6 +217,10 @@ obj vec<T> (Object, Serializable) {
for (var i = 0; i < dataIn.size; i++;) for (var i = 0; i < dataIn.size; i++;)
addEnd(dataIn[i]); addEnd(dataIn[i]);
} }
fun add_all_unique<U>(dataIn: ref vec<U>): void {
for (var i = 0; i < dataIn.size; i++;)
add_unique(dataIn[i]);
}
// same darn trick // same darn trick
fun add_unique<U>(dataIn: ref U): void { fun add_unique<U>(dataIn: ref U): void {
if (!contains(dataIn)) if (!contains(dataIn))