generics/arrays-not-supported-2.test \
generics/arrays-not-supported-3.test \
generics/class-property-override.vala \
+ generics/constraints.vala \
generics/constructor-chain-up.vala \
generics/delegate-return-type-missing.test \
generics/floating-type-cast.vala \
parser/foreach.vala \
parser/foreach-no-type.test \
parser/function-syntax-error.test \
+ parser/generics-constraints.vala \
parser/initializer-list-incomplete.test \
parser/inner-array-size.test \
parser/invalid-brace.test \
--- /dev/null
+class Base {
+}
+
+[GenericAccessors]
+interface IFoo<G> where G : Base {
+ public abstract G get (G g);
+}
+
+class Foo<T> : IFoo where T : Base {
+ public Base @get (Base g) {
+ return null;
+ }
+ public T @set (T t) {
+ return null;
+ }
+}
+
+class Bar<T> : IFoo where T : G {
+ public Base @get (Base g) {
+ return null;
+ }
+ public T @set (T t) {
+ return null;
+ }
+}
+
+class Manam : Foo {
+}
+
+delegate R FooFunc<R> (R r) where R : Base;
+
+S bar<S> (S s = null) where S : Base {
+ return null;
+}
+
+void main () {
+ var foo = new Foo ();
+ var faz = new Bar ();
+ var manam = new Manam ();
+ FooFunc func = () => { return null; };
+ bar ();
+}
--- /dev/null
+class Bar {
+}
+
+interface IFoo<G> where G : Bar {
+}
+
+class Foo<T> : Object, IFoo<T> where T : Bar {
+}
+
+struct FooStruct<T> where T : Bar {
+ public T t;
+}
+
+delegate T FooFunc<T> (T t) where T : Bar;
+
+T foo<T> (T t) where T : Bar {
+ return null;
+}
+
+void main () {
+}
}
}
- return null;
+ return get_constrained_type (type_param);
}
/**
}
if ((!allow_none || n_type_args > 0) && n_type_args < expected_n_type_args) {
- error = true;
- Report.error (source_reference, "too few type arguments for `%s'", type_symbol.to_string ());
- return false;
+ var type_params = ((GenericSymbol) type_symbol).get_type_parameters ();
+ bool mitigated = true;
+ foreach (var t in type_params) {
+ var ct = get_constrained_type (t);
+ if (ct != null) {
+ Report.notice (source_reference, "`%s' requires type arguments, constraining `%s' to `%s'", type_symbol.to_string (), t.name, ct.to_qualified_string ());
+ add_type_argument (ct);
+ } else {
+ mitigated = false;
+ }
+ }
+ if (!mitigated) {
+ error = true;
+ Report.error (source_reference, "too few type arguments for `%s'", type_symbol.to_string ());
+ return false;
+ }
} else if ((!allow_none || n_type_args > 0) && n_type_args > expected_n_type_args) {
error = true;
Report.error (source_reference, "too many type arguments for `%s'", type_symbol.to_string ());
return true;
}
+
+ DataType? get_constrained_type (TypeParameter type_param) {
+ unowned DataType? type = type_param.type_constraint;
+ if (type != null) {
+ return type.copy ();
+ }
+
+ return null;
+ }
}
type_arg = m.return_type.infer_type_argument (type_param, target_type);
}
+ unowned DataType? ct = type_param.type_constraint;
+ if (ct != null) {
+ Report.notice (source_reference, "`%s' requires type arguments, constraining `%s' to `%s'", m.to_string (), type_param.name, ct.to_qualified_string ());
+ type_arg = ct.copy ();
+ }
+
if (type_arg == null) {
error = true;
Report.error (ma.source_reference, "cannot infer generic type argument for type parameter `%s'", type_param.get_full_name ());
case TokenType.VOID:
case TokenType.VOLATILE:
case TokenType.WEAK:
+ case TokenType.WHERE:
case TokenType.WHILE:
case TokenType.WITH:
case TokenType.YIELD:
break;
}
break;
+ case TokenType.WHERE:
+ next ();
+ if (accept (TokenType.OPEN_PARENS)) {
+ rollback (begin);
+ parse_method_declaration (parent, attrs);
+ return;
+ } else {
+ rollback (begin);
+ switch (last_keyword) {
+ case TokenType.CLASS:
+ parse_class_declaration (parent, attrs);
+ return;
+ case TokenType.INTERFACE:
+ parse_interface_declaration (parent, attrs);
+ return;
+ case TokenType.STRUCT:
+ parse_struct_declaration (parent, attrs);
+ return;
+ default:
+ break;
+ }
+ }
+ break;
case TokenType.OPEN_PARENS:
rollback (begin);
if (!(parent is TypeSymbol)) {
case TokenType.STRUCT:
case TokenType.VIRTUAL:
case TokenType.VOLATILE:
+ case TokenType.WHERE:
return RecoveryState.DECLARATION_BEGIN;
case TokenType.BREAK:
case TokenType.CONTINUE:
base_types.add (parse_type (true, false));
} while (accept (TokenType.COMMA));
}
+ parse_type_parameter_constraints (type_param_list);
var cl = new Class (sym.name, get_src (begin), comment);
cl.access = access;
method.add_postcondition (parse_expression ());
expect (TokenType.CLOSE_PARENS);
}
+ parse_type_parameter_constraints (type_param_list);
if (!accept (TokenType.SEMICOLON)) {
method.body = parse_block ();
method.external = false;
if (accept (TokenType.COLON)) {
base_type = parse_type (true, false);
}
+ parse_type_parameter_constraints (type_param_list);
+
var st = new Struct (sym.name, get_src (begin), comment);
st.access = access;
if (ModifierFlags.EXTERN in flags) {
base_types.add (type);
} while (accept (TokenType.COMMA));
}
+ parse_type_parameter_constraints (type_param_list);
+
var iface = new Interface (sym.name, get_src (begin), comment);
iface.access = access;
if (ModifierFlags.EXTERN in flags) {
d.add_error_type (parse_type (true, false));
} while (accept (TokenType.COMMA));
}
+ parse_type_parameter_constraints (type_param_list);
expect (TokenType.SEMICOLON);
Symbol result = d;
}
}
+ void parse_type_parameter_constraints (List<TypeParameter> type_params) throws ParseError {
+ while (accept (TokenType.WHERE)) {
+ var begin = get_location ();
+ string id = parse_identifier ();
+ TypeParameter? type_param = null;
+ foreach (var t in type_params) {
+ if (t.name == id) {
+ type_param = t;
+ break;
+ }
+ }
+ if (type_param == null) {
+ Report.error (get_src (begin), "Unknown type parameter `%s'", id);
+ type_param = new TypeParameter (id, get_src (begin));
+ type_param.error = true;
+ }
+ expect (TokenType.COLON);
+ do {
+ /*
+ where T : <base class name>
+ where T : <interface name>
+ where T : U
+ */
+ begin = get_location ();
+ var type = parse_type (true, false);
+ type_param.type_constraint = type;
+ } while (accept (TokenType.COMMA));
+ }
+ }
+
void skip_type_argument_list () throws ParseError {
if (accept (TokenType.OP_LT)) {
do {
if (matches (begin, "using")) return TokenType.USING;
break;
case 'w':
- if (matches (begin, "while")) return TokenType.WHILE;
+ switch (begin[2]) {
+ case 'e':
+ if (matches (begin, "where")) return TokenType.WHERE;
+ break;
+ case 'i':
+ if (matches (begin, "while")) return TokenType.WHILE;
+ break;
+ }
break;
case 'y':
if (matches (begin, "yield")) return TokenType.YIELD;
d.accept_children (this);
}
+ public override void visit_type_parameter (TypeParameter p) {
+ if (p.checked) {
+ return;
+ }
+ p.accept_children (this);
+ }
+
public override void visit_block (Block b) {
if (b.checked) {
return;
VOID,
VOLATILE,
WEAK,
+ WHERE,
WHILE,
WITH,
YIELD;
case VOID: return "`void'";
case VOLATILE: return "`volatile'";
case WEAK: return "`weak'";
+ case WHERE: return "`where'";
case WHILE: return "`while'";
case WITH: return "`with'";
case YIELD: return "`yield'";
* Represents a generic type parameter in the source code.
*/
public class Vala.TypeParameter : TypeSymbol {
+ public DataType? type_constraint {
+ get {
+ if (_type_constraint is GenericType) {
+ return ((GenericType) _type_constraint).type_parameter.type_constraint;
+ }
+ return _type_constraint;
+ }
+ set {
+ _type_constraint = value;
+ if (_type_constraint != null) {
+ _type_constraint.parent_node = this;
+ }
+ }
+ }
+
+ DataType? _type_constraint;
+
/**
* Creates a new generic type parameter.
*
visitor.visit_type_parameter (this);
}
+ public override void accept_children (CodeVisitor visitor) {
+ if (_type_constraint != null) {
+ _type_constraint.accept (visitor);
+ }
+ }
+
+ public override void replace_type (DataType old_type, DataType new_type) {
+ if (_type_constraint == old_type) {
+ type_constraint = new_type;
+ }
+ }
+
/**
* Checks two type parameters for equality.
*