Bug 49784 - declaring interface methods should work as it does in interfaces
Summary: declaring interface methods should work as it does in interfaces
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Docs (show other bugs)
Version: 1.1.1   Edit
Hardware: PC Windows XP
: P2 major (vote)
Target Milestone: 1.2.1   Edit
Assignee: Erik Hilsdale CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-01-09 14:10 EST by Wes Isberg CLA
Modified: 2004-10-21 04:31 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Wes Isberg CLA 2004-01-09 14:10:50 EST
To declare a new interface method (not its implementation), being explicit works
but anything else results in non-obvious compiler or runtime errors.  Some
combination of more/better errors and applying the implicit interface modifiers
(public, abstract) is warranted.  i.e.,

------
interface I {}
aspect A {
    // only the first variant works
    public abstract int I.getInt();
    abstract int I.getInt();         // runtime AbstractMethodError
    int I.getInt();                  // weak compiler error
}
class C implements I {
   public int getInt() { return 1; }
}
------

Implicitly adding the public/abstract modifiers would make these like the Java
interface declaration that users already know.  (Conversely, declaring an
interface method private, protected, final, etc. should provoke a compiler error.)

See tests/bugs/interAbstract/InterfaceMethodDeclaration*.java
Comment 1 Jim Hugunin CLA 2004-01-12 04:43:05 EST
This would be a significant language change if we made it.  I'm marking as a 
P4 enhancement request for now.  If you think it should be higher priority, 
please discuss on users.

inter-type declarations on interfaces have much more flexibility that standard 
Java interface delcarations.  We allow all access modifiers (except protected) 
on these and allow non-abstract methods.  We believe this expansion is 
important to handle many crosscutting concerns well.

If we make public/abstract implicit, then it would be hard to specify non-
abstract methods, and it would be impossible to specify inter-type 
declarations with default visibility.

Clearly better error messages for these cases would be nice.
Comment 2 Wes Isberg CLA 2004-01-12 18:28:37 EST
Ok, three bugs here, that I should have written separately, as well as cleanly
distinguishing the case of declaring methods with bodies, which are not at issue
here.  The bugs:

(1) P2 fix the runtime error
(2) P3 better warning for leaving out the abstract modifier
(3) P2 resolve the language semantics and update the docs

(1) and (2) can be done with or without implicit hoisting, if you can implement
it.  

(1) If we are going to support "abstract int I.getInt();"  (default access on
abstract methods) without hoisting, let's do it.  If we can't, let's issue an
error (no hoisting) or hoist (with XLint warning).  In any case, this is a P2
because we're now producing bad bytecode.

(2) For "int I.getInt();" (no-abstract) case, it requires a better message
whether it's a better error or a hoisting with XLint warning, and thus a P3
(users can add comments to the bug if they want a higher priority).

For (1) or (2), we should do hoisting for both or neither for the sake of
specifying a consistent language rule.

As for whether adding public and/or abstract would be a language change
(ignoring that this is not yet specified):  Adding abstract is not a language
change for methods without bodies; that's either supported without message
(implicitly adding abstract for interfaces) or with message (error for classes).
 Similarly, (not) hoisting default to public is either supported without message
(because we can make default work) or with a message (error if we don't want to
hoist implicitly, or hoist implicitly with XLint warning).  In any case, there
is no behavior or semantics being prohibited (it just didn't work).

Here's a proposed semantics, to be added to the implementation section.  If we
can implement default abstract methods, then "{or default}" is included.

----
Declaring members of an interface from an aspect is similarly subject to
implementation limitations stemming from Java bytecode invariants.  Declarations
of methods without bodies on interfaces are implicitly public and abstract, as
they are on interfaces.  Specifying private {or default} access is permitted, 
but the declaration becomes subject to the limitations for a non-abstract
method.  If you declare a non-static field or a non-abstract method on an
interface, then all of the top-most implementors of that interface must be woven
by that same aspect. (A class C is a top-most implementor of an interface I if C
implements I and the superclass of C does not implement I.)
----

Following the model of combined bugs for now, I'm raising this to a P2 subject
to your fixing the (1) default-access case "abstract int I.getInt()".  If you
agree to the semantics, I'll just add them to implementation.xml (unless you or
Erik does).  Then you can leave this as a P3 if you don't get to case (2).  Or
split out the two other bugs if you like.

Comment 3 Jim Hugunin CLA 2004-01-14 06:39:23 EST
The actual bugs have been verified and fixed.  I'm now moving this to docs.  
The simple explanation is:

inter-type method declarations always have the same modifiers as amethod 
declaration would (except that protected is not permitted).  The target type 
of the inter-type declaration is irrelevant for the syntax of semantics of the 
declaration.  In particular, 'public abstract' is neve implied even if the 
target is an interface.
Comment 4 Wes Isberg CLA 2004-01-15 20:50:39 EST
"It's not dead yet!" :)

So now if I say
----
  interface I {}
  aspect A {
    abstract int I.i();
  }
  class C {
    int i() {}
  }
----
I get an error saying C must implement "int i()".  The error resolves if the
implementation in C is declared public. 
tests/bugs/interAbstract/InterfaceMethodDeclarationNonPublic.java

Should the error message should say that the declaration is implicitly public
and the implementation must be public? or is the error check wrong?.

Otherwise, I'd like to see an XLint warning for inter-type declarations on
interfaces of non-public abstract methods, which says that they are not
implicitly public as the user might have expected, both because interface
declarations normally are that way, and because it looks like other declarations
could be trouble for modular builds (and J2EE). To wit, the interface decompiles as:

interface I{
public abstract int ajc$interMethodDispatch2$$getInt();
}

This surprised me.  It means that interface now cannot be implemented by
anything outside the scope of the ajc compilation, and that all top-level
implementations of I *must* be recompiled with ajc.  That's a different thing
than saying for top-level types to inherit a method implementation, they have to
be in the scope of the compilation.  

Do they all have to be in the same compile, or can you compile the interface and
the (binary) aspect, and then put the interface on the classpath and the aspect
in the aspectpath and compile the other modules with the top-level classes? 
That's a pain, but it's doable.  If it has to be the same compile process, this
feature of ajc is unusable for J2EE.

Interfaces play the role of spanning namespaces that are built separately (e.g.,
an EJB and a servlet share the same business interface, but are built in
separate compiles).  If possible, we should preserve the ability to compile
interfaces and then EJBs, servlets, etc., through tool changes or documentation.
If it means that the only inter-type declaration on an interface that will work
in that context is of a public abstract method, we should document that. 
Because the "error" won't show up until a subsequent compile (pointing at a
funky ajc method name), we should also have an XLint message for any other
declaration, for people who want to avoid this problem before it happens.  



Comment 5 Andrew Clement CLA 2004-08-02 13:31:37 EDT
this bug is related to bug 70794 that I have just been working on.  Things seem
to have changed slightly since the last comment on this bug.  The program:

  interface I {}
  aspect A {
    abstract int I.i();
  }
  class C implements I{
    int i() {return 1;}
  }

gives the error: 
error must implement abstract inter-type declaration: int I.i()

And the program:
 interface I {}
  aspect A {
    abstract int I.i();
  }
  class C implements I{
    public int i() {return 1;}
  }

also gives the same error. (i.e. visibility modifier in the implementing class
doesn't make a difference).

I've just been working to fix the error in the case where an ITD puts an
abstract method in an interface and then an implementor of that interface *does*
provide an implementation.  My fix is nonsense I've discovered, but it did lead
me to this bug because the test for this bug (49784) failed in the harness when
I had my fix in place :)

I've also seen the situation Wes describes where you get 'funky' method
signatures in the interface if you didn't make your ITD public.  Tomorrow I will
see if this can be sorted out using a binary weaving approach like Wes talks about:

1) Compile aspect and interface
2) Build aspectpath zip containing the binary aspect
3) Build an implementor of the interface from source with the interface on the
classpath and the aspectpath containing the binary aspect.

At the moment I could be persuaded to put in an Xlint if we are producing funky
code that may byte the user later on :)
Comment 6 Andrew Clement CLA 2004-08-03 05:55:38 EDT
The steps I outlined in the last append:

1) Compile aspect and interface
2) Build aspectpath zip containing the binary aspect
3) Build an implementor of the interface from source with the interface on the
classpath and the aspectpath containing the binary aspect.

do not work.  You get an error something like:

C:\ajbugs\C.java:1 error Class must implement the inherited abstract method
I.ajc$interMethodDispatch2$$foo()
class C implements I {

1 error

when compiling the implementing class.
Comment 7 Erik Hilsdale CLA 2004-08-23 20:08:47 EDT
I'm closing this bug, as the resolution for bug 70794 handles this one as well.
In addition to the compiler limitiation added for that bug, I've added one
paragraph to the semantics document

        The access modifier of abstract inter-type methods has
        one constraint: It is illegal to declare an abstract
        non-public inter-type method on a public interface.  This
        is illegal because it would say that a public interface
        has a constraint that only non-public implementors must
        fulfill.  This would not be compatible with Java's type
        system.  
Comment 8 Adrian Colyer CLA 2004-10-21 04:31:52 EDT
Fix released as part of AspectJ 1.2.1