Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 440143

Summary: [1.8][null] one more case of contradictory null annotations regarding type variables
Product: [Eclipse Project] JDT Reporter: Stephan Herrmann <stephan.herrmann>
Component: CoreAssignee: Stephan Herrmann <stephan.herrmann>
Status: VERIFIED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: srikanth_sankaran
Version: 4.4   
Target Milestone: 4.5 M1   
Hardware: PC   
OS: Linux   
Whiteboard:
Bug Depends on:    
Bug Blocks: 438458    

Description Stephan Herrmann CLA 2014-07-22 12:20:09 EDT
Example from https://www.eclipse.org/forums/index.php/mv/msg/781138/1384369/#msg_1384369

//---
import org.eclipse.jdt.annotation.*;

public class Test7<@Nullable E> {
	E e;

	@Nullable
	E test() {
		return null;
	}

	@NonNull
	E getNotNull() {
		if (e == null)
			throw new NullPointerException();
		else
			return e;
	}
}
//---

triggers this error against getNotNull():

Contradictory null specification; only one of @NonNull and @Nullable can be specified at any location

Literally speaking this error can be regarded as correct, but we actually want to always consider '@NonNull E' as: "The @NonNull variant of whatever type E represents".
Hence this program should be legal, too.
Comment 1 Stephan Herrmann CLA 2014-07-22 14:42:18 EDT
Released for 4.5 M1 via http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=fa4debd5be46821e4afe7fa2082f7a976e89007c

While fixing this I found that typeSystem.getUnannotatedType() does not work for a TVB with declaration-site annotations, because typeSystem expects the naked type at position 0, but actually no unannotated type was registered. Fixed by cloning and registering a TVB before setting type annotations.
Comment 2 Srikanth Sankaran CLA 2014-08-05 05:23:59 EDT
(In reply to Stephan Herrmann from comment #0)
> Example from
> https://www.eclipse.org/forums/index.php/mv/msg/781138/1384369/#msg_1384369
> 
> //---
> import org.eclipse.jdt.annotation.*;
> 
> public class Test7<@Nullable E> {
> 	E e;
> 
> 	@Nullable
> 	E test() {
> 		return null;
> 	}
> 
> 	@NonNull
> 	E getNotNull() {
> 		if (e == null)
> 			throw new NullPointerException();
> 		else
> 			return e;
> 	}
> }
> //---
> 
> triggers this error against getNotNull():

Stephan, this program triggers:

Description	Resource	Path	Location	Type
Null type mismatch (type annotations): required '@NonNull E' but this expression has type '@Nullable E'	Test7.java	/P/src	line 16	Java Problem

against the "return e;" - Is this expected (with or without syntactic null
analysis turned on ?)
Comment 3 Stephan Herrmann CLA 2014-08-05 06:36:09 EDT
(In reply to Srikanth Sankaran from comment #2)
> (In reply to Stephan Herrmann from comment #0)
> > Example from
> > https://www.eclipse.org/forums/index.php/mv/msg/781138/1384369/#msg_1384369
> > 
> > //---
> > import org.eclipse.jdt.annotation.*;
> > 
> > public class Test7<@Nullable E> {
> > 	E e;
> > 
> > 	@Nullable
> > 	E test() {
> > 		return null;
> > 	}
> > 
> > 	@NonNull
> > 	E getNotNull() {
> > 		if (e == null)
> > 			throw new NullPointerException();
> > 		else
> > 			return e;
> > 	}
> > }
> > //---
> > 
> > triggers this error against getNotNull():
> 
> Stephan, this program triggers:
> 
> Description	Resource	Path	Location	Type
> Null type mismatch (type annotations): required '@NonNull E' but this
> expression has type '@Nullable E'	Test7.java	/P/src	line 16	Java Problem
> 
> against the "return e;" - Is this expected (with or without syntactic null
> analysis turned on ?)

Yes, this is expected.

Applying quick assist "invert if" resolves that issue, the resulting variant is detected by syntactic analysis:

//---
import org.eclipse.jdt.annotation.*;

public class Test7<@Nullable E> {
    E e;

    @Nullable
    E test() {
        return null;
    }

    @NonNull
    E getNotNull() {
        if (e != null)
            return e;
        else
            throw new NullPointerException();
    }
}
//---

I see that saying "has type @Nullable E" is not optimal, since no explicit annotation exists for 'e', but that's a result of the (necessarily) pessimistic interpretation of free type variables.

I'd argue that the following - though being "more correct" - would be overkill:
  "this expression can have type '@Nullable E'".

First: users still don't know what decides whether or not it has that type.

Second: I don't think we have the information in the compiler to distinguish "has type" vs. "can have type".

That's why I believe the current state is "good enough".
Comment 4 Srikanth Sankaran CLA 2014-08-06 02:00:22 EDT
Verified for 4.5 M1 using 
Version: Mars (4.5)
Build id: I20140804-2000