New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unexpected return.type.incompatible: Set<V extends @KeyFor...> vs Set<V> on return map.keySet()
#3638
Comments
Thanks for the bug report; we appreciate it. I'm sorry you are having trouble. For future reference, you can help us by giving a complete test case. This one is missing import statements, so we cannot compile it. Your original code must have contained those. The bug report as written forces us to hunt around for As a smaller issue, you can remove irrelevant fields like Here is an example of a reproducible test case: import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class DefaultDirectedGraph<V, E> implements DirectedGraph<V, E> {
final Map<V, String> vertexMap = new LinkedHashMap<>();
@Override
public Set<V> vertexSet() { // implement DirectedGraph method
return vertexMap.keySet();
}
}
interface DirectedGraph<V, E> {
Set<V> vertexSet();
} Another essential part of a bug report is to explain why you think an error or warning is a false positive. Otherwise we have to guess at your intention or your confusion. In general, the reported error indicates a potential bug, because Map<String, Integer> m = ...;
Set<@KeyFor("m") String> set1 = new HashSet<>(m.keySet());
Set<String> set2 = set1; // illegal
set2.add(someRandomValue); The type of
If you suppress warnings, there is the possibility of errors in your code, as shown above. Could you clarify "methods like that"? I am not sure what you have in mind. In the code snippet above, there is no possible error because of the unusual contract of keySet() that its return value does not support |
Here's a related example. Suppose there's a composite map that associates the same value for all the input keys: package org;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CompositeMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
private final List<K> keys;
private final V value;
public CompositeMap(V value, List<K> keys) {
this.value = value;
this.keys = keys;
}
@Override
public Set<Entry<K, V>> entrySet() {
Map<K, V> result = new HashMap<>();
for (K key : keys) {
result.put(key, value);
}
return result.entrySet();
}
public static void main(String[] args) {
System.out.println("set = " +
new CompositeMap<>(42, Arrays.asList("hello", "world")).entrySet()
);
}
} Apparently that does not quite work because the method must return Ok, let me try to add types: package org;
import org.checkerframework.checker.nullness.qual.KeyFor;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CompositeMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
private final List<@KeyFor("this") K> keys;
private final V value;
public CompositeMap(V value, List<@KeyFor("this") K> keys) {
this.value = value;
this.keys = keys;
}
@Override
public Set<Entry<@KeyFor("this") K, V>> entrySet() {
Map<@KeyFor("this") K, V> result = new HashMap<>();
for (@KeyFor("this") K key : keys) {
result.put(key, value);
}
return result.entrySet();
}
public static void main(String[] args) {
System.out.println("set = " +
new CompositeMap<>(42, Arrays.asList("hello", "world")).entrySet()
);
}
} It does not quite work, because, well, return result.entrySet();
^
type of expression: Set<Entry<K extends @KeyFor("result") Object, V extends Object>>
method return type: Set<Entry<@org.checkerframework.checker.nullness.qual.KeyFor({"this"}) K extends @KeyFor("this") Object, V extends Object>> and, what is more important, the class does not work from the user perspective: new CompositeMap<>(42, Arrays.asList("hello", "world")).entrySet()
^
found : List<String>
required: List<@KeyFor("this") String> I read this as "ok user, you must provide only those strings that are the keys of the map" which is very hard (impossible?) to do. |
Map<String, Integer> m = ...;
Set<@KeyFor("m") String> set1 = new HashSet<>(m.keySet());
Set<String> set2 = set1; // illegal
set2.add(someRandomValue); Just in case, * The set
* supports element removal, which removes the corresponding
* mapping from the map, via the <tt>Iterator.remove</tt>,
* <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
* <tt>clear</tt> operations. It does not support the
* <tt>add</tt> or <tt>addAll</tt> operations.
*
* @return a set view of the mappings contained in this map
*/
Set<Map.Entry<K, V>> entrySet(); The original code is indeed complicated, however, the intention and the reason I threat the warning as false positive is that In other words, I think I understand how I might reiterate on this topic a bit later. Calcite has ~5 or so |
Here's a part of the class in question
checker-framework issues error as follows:
I think it might be related to #1653, however, the difference here is that
Set<V>
signature is specified for a public API.The method itself looks very much
java.util.Map<K, V>.keySet()
which returnSet<V>
.Does that mean the methods like that always have to suppress
return.type.incompatible
error?The text was updated successfully, but these errors were encountered: