Bug 474965 - The assignment of null should be ambiguous
Summary: The assignment of null should be ambiguous
Status: NEW
Alias: None
Product: Xtend
Classification: Tools
Component: Core (show other bugs)
Version: 2.9.0   Edit
Hardware: PC Mac OS X
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-08-14 06:31 EDT by Anton Kosyakov CLA
Modified: 2019-07-14 08:38 EDT (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Anton Kosyakov CLA 2015-08-14 06:31:47 EDT
Xtend code:
val HashSet<String> values = newHashSet
values += null

The assignment should be ambiguous while there are 2 candidates which match:
1. org.eclipse.xtext.xbase.lib.CollectionExtensions.operator_add(Collection<? super E>, E)
2. org.eclipse.xtext.xbase.lib.CollectionExtensions.operator_add(Collection<E>, Iterable<? extends E>)

Currently the second variant wins and it leads to NPE at the runtime. It can be unexpected since HashSet permits null as a value.
Comment 1 Sebastian Zarnekow CLA 2019-07-14 08:38:04 EDT
Extension methods are not necessary to reproduce this:

import java.util.Set;
class C {
	def <T> void m(Set<? super T> target, T element) {}
	def <T> void m(Set<? super T> target, Iterable<? extends T> elements) {}
	def void m2(Set<String> strings) {
		m(strings, null);
	}
}

Java fags the equivalent code with an error, even though the error message is not really helpful, since 'null' is not necessarily a String:

The method m(Set<? super String>, String) is ambiguous for the type C

Stricter type bounds do not help in Xtend either:
import java.util.Set;
class C {
	def <T> void m(Set<T> target, T element) {}
	def <T> void m(Set<T> target, Iterable<T> elements) {}
	def void m2(Set<String> strings) {
		m(strings, null);
	}
}

Reason for the problem in Xtend:

On their own, both overloads of m are valid, so to figure the best match, the declared parameter types are taken into account. Since we use a type parameter T, its upper bound is used, which is Object. If the method signatures are compared, we see
m(Set, Object) and m(Set, Iterable). Since both overloads are valid for the arguments (Set, null), the most special declared type is used, which is (Set, Iterable).