Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

metamodel #226

Closed
gavinking opened this Issue · 110 comments
@gavinking
Owner

Implement the metamodel. Here's what needs to be done:

  • Define Ceylon interfaces
  • Provide Java implementations
  • Test (currently in compiler metamodel/runtime)
  • Support annotations
  • Support invoking method/constructors
  • Support invoking toplevel methods
  • Support reading/writing attributes
  • Support default arguments for methods/initialisers
  • Support variadic arguments for methods/initialisers
  • Support member classes/interfaces (and formal)
  • Support methods with multiple parameter lists (currently lost by the model loader)
  • Support local declarations -> moved to 1.0 #260
  • Rename to ceylon.language.model
  • Support object declarations
  • Support alias declarations
  • Unreify/erase private attributes/methods (even for Java types) -> Moved to #297
  • Add Class.parameters and Function.parameters -> Moved to 1.0 #296
  • Support union/intersection type members -> Moved to 1.0 #295
  • Java interop (to be expanded)
    • Java primitive types
    • Java properties
    • Java fields
    • Java static fields/methods -> Moved to 1.0 #294
    • Java overloaded methods/constructors -> Moved to 1.0 #293
    • Java inner class constructors (doesn't use factory methods unlike Ceylon)
  • Reify method/initialiser parameters
  • Reify class/interface/method type parameters
  • Reify module imports
  • Support dynamic loading of modules -> moved to 1.0 #280
  • Support looking up modules by name/version
  • Support looking up classes by name -> moved to 1.0 #282
  • Add distinction between declared/inherited members
  • Properly deal with locking
  • Implement verification of type constraints when applying types
  • Implement verification of reified type arguments
  • Implement verification of container types for memberApply
  • Implement hash/equals/string on the metamodel
  • Fix all remaining FIXME/TODO
  • Add attribute for getting fully qualified name (probably into Declaration)
  • Add attribute for getting container (probably into TopLevelOrMemberDeclaration)
  • Do not wrap exceptions in invocations
  • Add abstract, shared, actual, formal and default attributes to declarations
  • Deal with UnknownType -> moved to 1.0 #289
  • Add a way to get from a Member to its Declaration?
  • Implement the $call$variadic methods
  • Add is to Type?
  • Add a container type parameter to member apply
  • Change names to US-English
  • Check what happens WRT abstract classes and invoking their constructors
  • Figure out how to properly implement AliasDeclaration.apply/memberApply -> #298
@thradec
Collaborator

Will be possible to obtain some information about module?

@gavinking
Owner
@gavinking
Owner

Actually I think we should eliminate PackageMember and just give all Declarations a package attribute and a Boolean toplevel attribute.

@gavinking
Owner

A bug in the above design is that we can't get the annotations or name of a member declaration without providing an instance of the type. Need to fix that.

@quintesse
Collaborator

Maybe add some utility attributes to Declaration for the annotations defined by the language? Like shared, default, variable, etc?

And what is the annotatedMembers for?

@FroMage
Owner

I'd like to see:

  • modules, and a way to list modules
  • looking up modules, packages, types by name
  • shortcuts for predefined annotations like Boolean formal (and same for shared, etc…)
  • a way to get the topmost refined method/attribute
  • a way to list declared members as well as flattened members (not sure what the current members refers to)
  • a way to get the parameters for a method, its return type, its type parameters
  • same for a class, for its initialiser
  • a way to get the type parameters for a ClassOrInterface
  • a way to get the attribute type
  • I think we need to think about members/containers a bit more because classes/interfaces/methods may be contained by a method, not just a type
  • shortcuts to get a Declaration's package/module, rather than walk the sequence of containers all the way up
  • info to know -- if a type is an anonymous class -- type parameter constraints
    • case classes
    • self bounds
@FroMage
Owner

And:

  • for parameters:
    • defaulted or not
    • I guess a Callable or something that gives me the default value
    • variadic or not
    • name
@gavinking
Owner

what is the annotatedMembers for?

To query for all members with a certain annotation.

@FroMage
Owner

One thing to keep in mind: we're hiding Java's Object.getClass() and T.class literals ATM so we will need a way to either unhide them, or convert from a Ceylon Type to a Java Class, because there are many Java APIs that require Class instances.

@gavinking
Owner

a way to get the type parameters for a ClassOrInterface

Well so the above design doesn't address the issue of type constructors. That's an issue I need to put some extra thought into. A ClassOrInterface would be a type with arguments.

@gavinking
Owner

I think we need to think about members/containers a bit more because classes/interfaces/methods may be contained by a method, not just a type

I'm assuming that local declarations don't reify their container.

@FroMage
Owner

I'm assuming that local declarations don't reify their container.

Well in the case of methods, they do exist as type objects, so why not?

@FroMage
Owner

BTW:

interface Member<Type,Kind> 
        satisfies Kind(Type) // WTF???
@gavinking
Owner

WDYM WTF?

If you have a Member, you can invoke it, passing an instance of the type it is a member of, and get back a Function or Value.

@FroMage
Owner

We support these kinds of constraints since when?

@FroMage
Owner

Oh wait. This is the shortcut for Callable<Kind,[Type]>? I thought it was a type constructor constraint :)

@gavinking
Owner

No, that would be given Kind(Type t), not satisfies Kind(Type). And I think we've decided against adding those :)

@FroMage
Owner

OK then why do we prefer to invoke the member to get the good stuff rather than make it an attribute or better a sub-type?

@gavinking
Owner

As I noted above:

A bug in the above design is that we can't get the annotations or name of a member declaration without providing an instance of the type. Need to fix that.

@RossTate
Collaborator

Technical comment: Declaration should be of ... Function<Anything,Nothing> ... and similarly elsewhere.

@gavinking
Owner

@RossTate eh? why?

@gavinking
Owner

@RossTate Oh, you're right. I want the lower bound not the upper bound. Oops.

@FroMage
Owner

Hum. I don't quite see how I can get started on implementing that part without the reified generics part. IMO we need the same sort of abstraction as ProducedType, and we're missing union and intersection types too. I'm not quite sure how we can fit all that together, so I'll let you propose what you had in mind.

@FroMage FroMage referenced this issue in ceylon/ceylon-compiler
Closed

Implement the metamodel #1126

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage First metamodel test #226 8ca479c
@FroMage FroMage referenced this issue from a commit in ceylon/ceylon-compiler
@FroMage FroMage Moved metamodel utilities ceylon/ceylon.language#226 fc57dc3
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: added resolved superclass/interfaces to ClassOrInterfaceType
#226

as opposed to the unresolved (including free TypeParameter) in ClassOrInterface
1fd65e0
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: support the Nothing type #226
not sure at all if it should be
shared object nothingType satisfies ProducedType{}
or
shared interface NothingType of nothingType satisfied ProducedType{}
shared object nothingType of NothingType{}
c698df0
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: refactored boxing of return value into MethodHandleUtil an…
…d use for AppliedFunction invocation #226
62b3353
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: added note #226 b66fb8d
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: big refactoring #226
Split the untyped declaration part into untyped/ and the applied types stay in c.l.metamodel and change
named to the shorter version.
Reinstate Member because it's necessary.
Renamed AppliedProducedType to AppliedType.
74f9856
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: turned Value.applied into Value.apply(Anything instance = …
…null) #226

Because otherwise we can't represent reflection Value without instances around,
as would be required by Member
212e8b2
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: preliminary support for member types #226
we can now instantiate member classes (in classes)
f36f53e
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: find the corrent container class for interface member clas…
…ses #226

the $impl was not good, we needed the original interface, which we can obtain via the typechecker
model container
bb5c47f
@FroMage
Owner

How we get from method declarations (untyped) to method metamodels (applied) leaves a lot to be desired, because I currently have:

interface Package {
 shared formal Function? getFunction(String name);
}
interface Function {
 shared formal AppliedFunction<Anything, Nothing> apply(AppliedType* types);
}

And nothing for member functions, which need an extra container instance.

By comparison, for the method metamodel we have:

interface ClassOrInterface<T> ... {
    shared formal Member<SubType, Kind>? getFunction<SubType, Kind>(String name, AppliedType* types)
        given Kind satisfies Function<Anything, Nothing>;
}

So it is the responsibility of the container to provide the applied function. We could apply the same technique to the untyped part:

interface Package {
 shared formal Kind? getFunction<Kind>(String name, AppliedTypes* types)
        given Kind satisfies AppliedFunction<Anything, Nothing>;
}
interface Function {
}
interface ClassOrInterface ... {
    shared formal AppliedMember<SubType, Kind>? getFunction<SubType, Kind>(String name, AppliedType* types)
        given Kind satisfies AppliedFunction<Anything, Nothing>;
}

But then we're left with no way to get a metamodel function from its untyped function. If we wanted that, we'd need either two function interfaces (toplevel and member), or an optional instance parameter:

interface Function {
 shared formal AppliedFunction<Anything, Nothing> apply(Object? instance = null, AppliedType* types);
}

But that's really lame, especially given that positional invocation would require function.apply(null, type<String>()) and named invocation doesn't help because magic is only done for Iterable: function.apply{ types = {type<String>()};}.

Any opinion?

@FroMage
Owner

Amazingly, @tombentley is not part of this issue yet ;)

@tombentley
Collaborator

@tombentley recognises a can of worms when he sees one :-) Anyway, he has a wormery of his own:

  1. How to constrain annotation types to program elements with contravariant type parameters when something like shared annotation class Abstract() satisfies OptionalAnnotation<Abstract, Class<Anything, Nothing>> {} results in the typing error "error: type with contravariant type parameter Class appears in contravariant location in supertype: OptionalAnnotation>"

  2. What ceylon.language.metamodel::Annotation is for, when annotations(), optionalAnnotation() and sequencedAnnotations() each deal only with it's subclass ceylon.language.metamodel::ConstrainedAnnotation. Can @gavinking enlighten me?

  3. How to remove the commented out constraint (of OptionalAnnotation<Value,ProgramElement> | SequencedAnnotation<Value,ProgramElement>) in ConstrainedAnnotation when the compiler complains "error: case type must be a subtype of enumerated type: OptionalAnnotation is not assignable to ConstrainedAnnotation" for reasons I can't quite fathom.

  4. Whether we actually need ceylon.language.metamodel::Type, and if we do how it relates to ceylon.language.metamodel::AppliedType.

All these aside I have somewhat working implementations of optionalAnnotation() and sequentialAnnotations().

@FroMage
Owner

Go to hell. I wanted answers, not questions. If I wanted questions I'd have gone to church…

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: made Function.parameterLists Sequential->Sequence bec…
…ause there must be at least one
1d1cfde
@tombentley
Collaborator

According to the grammar a setter can have its own annotation list, but unless a setter is represented in the metamodel independently of a value (i.e. not as a subtype of Value) then as far as I can see there's nothing we can pass to annotations() to obtain those annotations at runtime.

It would be really unnatural to have Variable not be a subtype of Value though. We could have an Annotated attribute on Variable for getting the setter annotations, however we'd still need a Setter metamodel interface to allow people to constrain an annotation type to setters only.

@FroMage
Owner

Remind me why we have annotations in the first place? As opposed to Declaration.annotations()? Because we could have Variable.setterAnnotations to complement Attribute.annotations.

@tombentley
Collaborator

I'm working mostly on the stuff that's been the language module since always.

Not every annotated thing is a Declaration (e.g. Module, Package, module imports). You'd need to have the method on Annotated so you could abstract across annotated things. Then Variable.annotations() would be the getters annotations and Variable.setterAnnotations would have to be an Annotated, so you could call Variable.setterAnnotations.annotations().

But that leaves my point about how to contain an annotation class to setters.

@FroMage
Owner

Annotated could indeed have shared formal Annotation[] annotations; but we don't need Variable.setterAnnotations to be one, it just needs to be an extra shared formal Annotation[] setterAnnotations; attribute.

@tombentley
Collaborator

Sure we could do that, but it destroys the purpose of Annotated as the way of abstracting over things which can be annotated.

@FroMage
Owner

Oh, you mean as a way of limiting things which can be annotated?

@FroMage
Owner

So, to get back to Function, here's what I just added:

shared interface Function {
    shared formal AppliedFunction<Anything, Nothing> apply(AppliedType* types);

    shared formal AppliedFunction<Anything, Nothing> bindAndApply(Object instance, AppliedType* types);

    shared formal AppliedMember<Container, Kind> memberApply<Container, Kind>(AppliedType* types)
        given Kind satisfies AppliedFunction<Anything, Nothing>;
}

So I've separated apply for toplevel and member, and added a way to get an applied member too. We'll see if that works out.

@FroMage
Owner

@RossTate:

given class Foo<X>(){}, is there any consecrated terminology to express the difference between:

  • the Foo declaration,
  • the Foo<X> type when X may be free, and
  • the Foo<String> type where they may not be any free type variables?

We've got a bit of a terminology problem here.

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: applied the same recipe to ClassOrInterface as for Fu…
…nction to get applied versions and supporting member types
47b0613
@RossTate
Collaborator

I would say Foo is typically called a type constructor. Foo<X> and Foo<String> are both instantiations of Foo (and are types), hence the terms single/principal/multiple-instantiation inheritance. Foo<String> is called a closed type (at least an expression with no free variables is called a closed expression).

@FroMage
Owner

So let's see if the following rename would help clear things up:

in ceylon.language.metamodel.declaration, which represents type declarations and types with free type variables (formerly ceylon.language.metamodel.untyped):

  • Declaration (unchanged)
  • ClassOrInterfaceDeclaration (formerly ClassOrInterface)
  • ClassDeclaration (formerly Class)
  • InterfaceDeclaration (formerly Interface)
  • TypeParameterDeclaration (formerly TypeParameter)
  • FunctionDeclaration (formerly Function)
  • AttributeDeclaration (formerly Value)
  • PackageDeclaration (formerly Package)
  • ModuleDeclaration (formerly Module)
  • OpenType (formerly Type)
  • OpenIntersection (formerly IntersectionType)
  • OpenUnion (formerly UnionType)
  • OpenParameterisedType (formerly ParameterisedType)
  • OpenTypeVariable (formerly TypeParameterType)

in ceylon.language.metamodel (which represents closed types and declarations with only closed type variables applied):

  • DeclarationType (formerly Declaration)
  • ClassOrInterfaceType (formerly ClassOrInterface)
  • ClassType (formerly Class)
  • InterfaceType (formerly Interface)
  • FunctionType (formerly Function)
  • ValueType (formerly Value)
  • VariableType (formerly Variable)
  • Type (formerly AppliedType)
  • UnionType (unchanged)
  • IntersectionType (unchanged)

Would that be better?

@tombentley
Collaborator

Overall I think it would.

  • Possibly DeclaredType makes more sense to me than DeclarationType.
  • What's happened to untyped.Parameter?
  • Would ModuleDeclaration be a subtype of Declaration?
  • Are we definitely going with "Function" rather than "Method"?
@FroMage
Owner
  • Possibly DeclaredType makes more sense to me than DeclarationType.

Perhaps

  • What's happened to untyped.Parameter?

Turns into ParameterDeclaration, I guess.

  • Would ModuleDeclaration be a subtype of Declaration?

We can't because that would mean it could be contained within a package, class or interface. Or we need another interface.

  • Are we definitely going with "Function" rather than "Method"?

I think so, because technically toplevel functions are not methods.

@tombentley
Collaborator

We can't because that would mean it could be contained within a package, class or interface. Or we need another interface.

Then imho it's confusing that it's called ModuleDeclaration, rather than just Module, or ModuleDescriptor or something.

Similarly if Parameter won't be satisfying Declaration then ParameterDeclaration would be a confusing name for it.

@tombentley
Collaborator

Yesterday I whiled away some time trying to change the annotations() top level function to be a member of Annotated, before I gave up. The problem was that annotatations() uses a type parameter for the ProgramElement type argument to ContrainedAnnotation. If I make annotations() a member, that type argument needs to be the type of the Annotated instance. In other words, I have to make Annotated a self type. That change ripples though every other annotation-related declaration and we wind up needing to have type parameters on annotation classes and constructors. That's not something we've contemplated before, and while I'm sure we could make it work out, I'm not sure that we end up gaining anything that's worth the effort.

I also took the decision to remove the satisfies Annotated from the Applied* interfaces in ceylon.language.metamodel, so just the relevant interfaces in ceylon.language.metamodel.untyped satisfy Annotated. I think it makes more sense for just the declarations to be Annotated, because, well, it's the declarations of things which are annotated (e.g. interface List<Element>...), not the produced references to those things (List<String>). It also solves the problem I was having with the variances of the parameterized 'applied' interfaces, since the 'declaration' interfaces are not parameterized. This means I can now constrain the annotation classes in the language module to the right program elements (at least as much as is possible without a toplevel/member distinction in the subtypes of Annotated).

@tombentley
Collaborator

We have Package.members() and untyped.ClassOrInterface.members(), but not the obvious Package.member(String) and untyped.ClassOrInterface.member(String) for getting the member declaration with the given name (and the declaration kind according to the type argument). I guess it's also worth having annotatedMember(String) for consistency with annotatedMembers() and since that gives a simple way of finding, say, the shared declaration with a given name.

@tombentley
Collaborator

Yesterday I implemented Package.annotatedMembers() and ClassOrInterface.annotatedMembers() and it made me realize a couple of things:

  1. Things aren't ideal for the use case where you want to find all the callable methods in a class, or all the callable top level things in a package. Consider:

    Package pkg = ...
    value callables = pkg.members<ClassDeclaration|FunctionDeclaration>();
    

    Unfortunately callables needs further filtering by the caller because it will include abstract classes. A similar problem exists members where we cannot express (via a type argument to members() or annotatedMembers()) that we want declarations without the formal annotation.

  2. Using type arguments to express conditions about the members to be returned is going to mislead people. I can well imagine someone reasoning that if cls.annotatedMembers<FunctionDeclaration, Shared|Doc>() returns the methods which have shared or doc annotations then cls.annotatedMembers<FunctionDeclaration, Shared&Doc>() must return the methods which have both shared and doc annotations. Unfortunately it doesn't do that: It returns the methods with have an annotation which satisfies Shared&Doc (=Nothing since they're classes) which is always empty.

(More generally I'm unclear about when it's preferred to use a type argument to pass type information -- as we're going in Package.annotations() etc -- and when it's preferred to use a metamodel-typed value argument.)

In any other API it would be natural to use a comprehension to do this filtering. We'd need a method to test the the presence of an annotation of a certain type:

    {for (method in cls.members<FunctionDeclaration>()) 
        if (method.hasAnnotation<Shared>() && method.hasAnnotation<Doc>()) 
            method } 

Just thinking out loud here...

@FroMage
Owner

We definitely need hasAnnotation but should it be hasAnnotation<T>() given T satisfies Annotation or hasAnnotation(ClassOrInterface<Annotation> type)?

@FroMage
Owner

@gavinking: unless you refuse the rename I proposed, I will implement it tomorrow.

@FroMage
Owner

Just pushed a huge refactoring of the names, will post more details tomorrow.

@FroMage
Owner

So here's the gist of it.

In ceylon.language.metamodel:

  • Type represents the top of all closed types (the previous unused Type was removed and this is now the old AppliedType), such as UnionType, IntersectionType (I'm tempted to rename them to remove the trailing Type), nothingType and ClassOrInterface.
  • DeclarationType is the top of all closed declarations, such as ClassOrInterface, Function and Attribute (previously Value).
  • ClassOrInterface is both a Type and DeclarationType, and has two subtypes Class and Interface.
  • Attribute has a subtype Variable

In ceylon.language.metamodel.declaration (formerly untyped):

  • Declaration is the top of all declarations, such as AttributeDeclaration (and its sub type VariableDeclaration), FunctionDeclaration and ClassOrInterfaceDeclaration (and its sub types ClassDeclaration and InterfaceDeclaration)
  • GenericDeclaration is an interface that represents a Declaration with type parameters (ClassOrInterfaceDeclaration and FunctionDeclaration)
  • I will add another interface to represent declarations that have parameters.
  • OpenType is the top of all open types, such as OpenUnion, OpenIntersection, nothingType (yeah, another one, perhaps we need to change its name), OpenParameterisedType (a ClassOrInterfaceDeclaration with type arguments) and OpenTypeVariable (I'm a bit tempted to merge this with TypeParameter but they can't be both case types then)
  • SetterDeclaration is something that I frankly don't know how to classify
  • AttributeDeclaration, Module, Package and TypeParameter I'm not too sure about the names
  • I wonder if we shouldn't need a supertype of Declaration which would have a name and list of annotations and would include AttributeDeclaration, Declaration, Package and Module (can type parameters have annotations?)

Overall, I think the API is getting clearer, because we have declarations, open types and closed types and the terminology and difference is easy to explain.

@FroMage
Owner

As I was trying to use the metamodel to print the annotations on every declaration it occured to me that we can only list the instances of each annotation, which gives us their type, and using the metamodel we can obtain their attributes, but we can't figure out their annotation constructor, so we can't use the metamodel to print out the signature of Ceylon types with their annotations. I think that's an issue.

@FroMage
Owner

How do we call the set of declarations which can be both toplevel and members? Classes, interfaces, attributes and functions. As opposed to function/class parameters and type parameters which I strongly feel are also declarations.

@gavinking
Owner

we can't use the metamodel to print out the signature of Ceylon types with their annotations. I think that's an issue.

To be honest I don't really see why it needs to be an issue...

@FroMage
Owner

Or would be an issue when implementing a code generator or a ceylondoc tool.

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: added FunctionalDeclaration interface which has a lis…
…t of parameters

we don't need more lists since at runtime the other lists disappear
8e17a9f
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: pulled annotations() to AnnotatedDeclaration and impl…
…ement it in module/package/parameter
c5af0be
@FroMage
Owner

I've renamed Declaration to TopLevelOrMemberDeclaration, which is the best I could find, but feel free to suggest better.

I've introduced Declaration which has a name, AnnotatedDeclaration which is a Declaration & Annotated, and now Package, Module, AttributeDeclaration and TopLevelOrMemberDeclaration are AnnotatedDeclaration.

@FroMage FroMage referenced this issue from a commit in ceylon/ceylon-spec
@FroMage FroMage moved a method from ExpressionVisitor -> Util so the runtime can use …
…it for defaulted parameters in metamodel ceylon/ceylon.language#226
b18dadb
@tombentley
Collaborator

How are we handling object declarations in the metamodel? At the moment we seem to be treating them as ClassDeclarations with anonymous=true, which is fine, but then how do you get the value?

It would seem natural to treat it as a ClassDeclaration&AttributeDeclaration, but that doesn't work because they're both cases of TopLevelOrMemberDeclaration, thus disjoint so their intersection is empty.

@tombentley
Collaborator

We could:

  • add a c.l.m.d.TypeDeclaration of ClassOrInterfaceDeclaration | ObjectDeclaration (and move superclass, interfaces, members(), annotatedMembers() and getMember() from ClassOrInterfaceDeclaration to this) and
  • add a TypedDeclaration of AttributeDeclaration | FunctionDeclaration | ObjectDeclaration (and move the type to this)
  • and add ObjectDeclaration as a case of TopLevelOrMemberDeclaration
@FroMage
Owner

To me object declarations are just attributes with an anonymous type. I don't think we should make them anything special because they are not, they are syntactic sugar. To get the value you can only do that if it is indeed an attribute which has a reified metamodel, like toplevels or members (and then you need an instance), but never for locals (the type is reified, not the local attribute).

Also you can't have type X as enumerated subtype of two separate types ;)

So I suggest we leave it as it is now.

@tombentley
Collaborator

Also you can't have type X as enumerated subtype of two separate types ;)

Sure you can!

Anyway, the problem with leaving it as it is is that there are then two ways of accessing something that it really a single thing. In particular I can write:

value docs1 = annotations(docAnnotation, aPackage.getAttribute("myObject"));
value docs2 = annotations(docAnnotation, aPackage.getClassOrInterface("myObject"));

Right now those don't return the same thing, because in the one case we're looking for the annotations on the class, and in the other we're looking for the annotations on the getter. There are other places where arbitrary weirdness will occur, what is members<TopLevelOrMemberDeclaration>() supposed to do? Does it return just one element for an object (if so, which one) or does it return two?

@FroMage
Owner

Sure you can!

Last time I tried it we couldn't.

@quintesse
Collaborator

Well, to me an object, being anonymous should not appear as a ClassOrInterface in the members list nor should you be able to do aPackage.getClassOrInterface("myObject").

It would seem natural to treat it as a ClassDeclaration&AttributeDeclaration, but that doesn't work because they're both cases of TopLevelOrMemberDeclaration, thus disjoint so their intersection is empty.

I'm not sure I understand, this should be possible, right? I just did a tiny test to make sure and intersections of interfaces inheriting from the same base interface seem to work just fine.

@tombentley
Collaborator

The problem is that TopLevelOrMemberDelcaration enumerates its subtypes, and those are required to be disjoint, so it then uses that disjointness to prove that the intersection is empty.

@FroMage
Owner

We can certainly make the type disappear from members and getClassOrInterface and stuff.

@tombentley
Collaborator

Well that's a terrible idea, because an object does define a type, it's just a type without a name. It means you won't be able to get the members of the object, for example.

@quintesse
Collaborator

The problem is that TopLevelOrMemberDelcaration enumerates its subtypes

Exactly, maybe that is the problem ;)
If an object really conceptually is both a class and an attribute than also in the real world it's not "disjunct", so maybe we shouldn't try to model declarations that way either?

it's just a type without a name

So you shouldn't be able to get to it using "myobject", because that is not its name!

To me the idea would be that it's both a class and an attribute like you said before, which then let's you get the attribute and "cast" it to a class, thereby getting at the class information you want.

@FroMage
Owner

yes we should not be able to get it by name. I don't see an issue with listing it as a member.

@tombentley
Collaborator

Seriously, you don't find it inconsistent that an object would be in the result of members<ClassOrInteraceDeclaration>(), and it has a name attribute, but you can't get it by that name?

Or that members<TopLevelOrMemberDeclaration>() has two distinct elements with the same name?

Or that it would be missing from annotatedMembers<ClassOrInteraceDeclaration, Shared>() but present in members<ClassOrInteraceDeclaration>(), even though it's type is shared?

@FroMage
Owner

I'm not sure what the target of those annotations are, really. Is it the attribute or its type?

@tombentley
Collaborator

I'm not sure what the target of those annotations are, really. Is it the attribute or its type?

Well the old model annotations (@.com.redhat.ceylon.compiler.java.metadata.Annotations) were being applied to both the class and the getter. I don't think that's really a good idea. At the moment the user annotations are on the getter.

I tried removing the of clause from TopLevelOrMemberDeclaration and adding an interface ObjectDeclaration satisfies AttributeDeclaration & ClassDeclaration and it works out well. The only existing code which needed to be changed was the visitor in the metamodel tests, where a switch could not longer be used. In terms of implementation, my FreeObject just delegates to a FreeAttribute or FreeClass. Unless @FroMage actively objects to this (no pun intended), I think it's worth doing.

@FroMage
Owner

I'm really not convinced this is a good idea at all. I also haven't seen any compelling justification for doing so that we haven't rebuked. And I don't like getting rid of the case types. It's also not correct because you will get types that are ObjectDeclaration for anonymous local variables, but you won't be able to satisfy the AttributeDeclaration part of the declaration where you can get the value because it's not reified.

@tombentley
Collaborator

Actually Stef you've hardly put forward any arguments at all, and you've ignored my points about the inconsistencies of keeping it how it is.

We've not yet really thought about how locals fit into the metamodel, but we can always just treat those as ClassDeclarations. Moving the user annotations onto the type is not a big deal at all.

@FroMage
Owner

I've responded to every point you made. We said it would be logical to not let you refer to the object declaration by name, because the name is just a convenience. We also said it was not surprising to let you list them with members and annotatedMembers. What other point is there? You seem to think objects are a type of declaration that is both an attribute and a class, and that's OK, it's one way of looking at it. But it's not any more logical or true than the way we've been modeling them so far as an attribute with an anonymous class that you can't refer to by name. Objects are nothing more than syntactic sugar IMO, and that's not any less valid than your POV of merging the two.

Except that for objects where there is a type and no reified attribute, where your POV is not implementable.

locals don't fit in the metamodel, but their types do, because you can always obtain the type of local objects and you will get a ClassDeclaration that can't possibly be an AttributeDeclaration because we don't reify those and we can't get an Attribute from it anyways.

@FroMage
Owner

As for the annotations of object declarations it's really not clear to me whether they apply to the attribute, its declaration or both. It is going to be problematic for annotations which are constrained. I guess shared actual and friends really apply to the attribute, but perhaps in the future we can imagine we want type annotations on anonymous classes? Note that Java doesn't allow those AFAIK, so perhaps we should just bind them to the attribute and the class can't have any.

@gavinking
Owner

To me object declarations are just attributes with an anonymous type. I don't think we should make them anything special because they are not, they are syntactic sugar.

Agreed.

@gavinking
Owner

Also you can't have type X as enumerated subtype of two separate types ;)

I had to disallow it because it resulted in probably undecidability.

@gavinking
Owner

Well that's a terrible idea, because an object does define a type, it's just a type without a name. It means you won't be able to get the members of the object, for example.

I don't see this as a very big problem. Eventually we will let you denote the type of an object. Perhaps even just by writing \Iprocess, and then you can write this:

Type<\Iprocess> processType = `\Iprocess`;

but in the meantime, I don't think it's a big deal.

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel: FreeModule #226: fixed obtaining the list of packages
we don't want the imported packages
2e3fda7
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: added FIXME be1d673
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226 Util: added toArray that uses reflection to instantiat…
…e the array type

I need this for the metamodel
4bd5bf0
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: fixed the second parameter of isReifiedTypeSupported
To actually only skip the first parameter when it is synthetic
e5720dc
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: do not require @Ignore annotation on TD parameter if …
…the whole method is ignored
a4e8e7d
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: generate a defaulted method for java variadic methods
because our calling convention is that variadic methods have (at least) their last parameter defaulted
e9f51d4
@FroMage
Owner

So, is this what you had in mind for toplevels vs members, Gavin?

import ceylon.language.metamodel {
    ClosedType = Type
}

shared interface AttributeType<out Type>
        satisfies DeclarationType {

    shared formal actual AttributeDeclaration declaration;

    shared formal ClosedType type;
}

shared interface Value<out Type>
        satisfies AttributeType {

    shared formal Type get();
}

shared interface Attribute<in Container, out Type>
        satisfies AttributeType & Member<Container, Value<Type>> {
}

shared interface FunctionType<out Type, in Arguments>
        satisfies DeclarationType
        given Arguments satisfies Anything[] {

    shared formal actual FunctionDeclaration declaration;

    shared formal ClosedType type;
}

shared interface Function<out Type, in Arguments>
        satisfies FunctionType<Type, Arguments> & Callable<Type, Arguments>
        given Arguments satisfies Anything[] {
}

shared interface Method<in Container, out Type, in Arguments>
        satisfies FunctionType<Type, Arguments> & Member<Container, Method<Type, Arguments>>
        given Arguments satisfies Anything[] {
}
@gavinking
Owner

looks about right, yes. but still missing Class/MemberClass.

@FroMage
Owner

OK good, yes that one is coming too, just wanted to make sure the concept was what you had in mind.

@FroMage
Owner

Note that I just realised that Member can't be a Kind(Type), it has to be a Kind(Type,ClosedType*) to get type arguments for type-parameterised members.

@gavinking
Owner

P.S. I don't think it's a big deal if Member has a method or methods rather than directly satisfying Callable, if that turns out to be an issue.

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: Renamed ClassOrInterfaceDeclaration methods to avoid …
…name clashes with ClassOrInterface
435254a
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: got rid of container on Member
I couldn't manage to make the API work with it at this point, I'll try to reenable it later if I can
f023217
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: fixed CCE 4c581f6
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: added FIXME 0ef6058
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226 ceylon/ceylon-compiler#1197: move to ProducedTypedRefe…
…rences for applied attributes/methods
efa2fc5
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226 ceylon/ceylon-compiler#1197: applied method/attribute …
….string prints container and type args
7b59c3d
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: added FIXME ced2963
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: added FIXME 1536a2f
@FroMage FroMage was assigned
@thradec
Collaborator

I added following points into issue todo list:

  • Add attribute for getting fully qualified name (probably into Declaration)
  • Add attribute for getting container (probably into TopLevelOrMemberDeclaration)
  • Add InvocationTargetException
  • Add ClassDeclaration.abstract attribute
@FroMage
Owner

Mmmm, now that the mixins are gone perhaps we can get rid of the -Declaration postfix to every Declaration (and subtypes) methods? They were added to resolve mixin ambiguity that doesn't exist anymore.

@tombentley
Collaborator

Well we still have things like model.Class and model.declaration.ClassDeclaration, don't we? I know we can use an import alias, but won't these be used together often enough that it's worth having their simple names different to save people having to do that? And although it's more typing, I find the Declaration suffix to be helpful in keeping in mind what's a declaration and what's not.

@FroMage
Owner

No, only for methods and attributes, not for the type names :)

@tombentley
Collaborator

They are all called declaration aren't they? Could you give an example, I'm clearly being dense.

@FroMage
Owner
shared interface ClassOrInterfaceDeclaration 
        of ClassDeclaration | InterfaceDeclaration 
        satisfies TopLevelOrMemberDeclaration & GenericDeclaration {

    shared formal OpenParameterisedType<ClassDeclaration>? superclassDeclaration;
}

It used to be ClassOrInterfaceDeclaration.superclass but we moved all members to have the -Declaration postfix. I'm proposing to remove it back.

@quintesse
Collaborator

Yeah, probably nice, the postfix is superfluous now.

@tombentley
Collaborator

I think that would be better.

@gavinking
Owner

Rm, I would leave the Declaration suffix personally.

@FroMage FroMage referenced this issue from a commit in ceylon/ceylon-compiler
@FroMage FroMage metamodel ceylon/ceylon.language#226: test actual/abstract/formal/def…
…ault/shared attributes
f7f833e
@FroMage FroMage referenced this issue from a commit in ceylon/ceylon-compiler
@FroMage FroMage metamodel ceylon/ceylon.language#226: check that a runtime exception …
…is gone
7e289bf
@FroMage FroMage referenced this issue from a commit in ceylon/ceylon-compiler
@FroMage FroMage metamodel ceylon/ceylon.language#226: test containers b427c86
@FroMage
Owner

So @thradec: why again is InvocationTargetException better to be thrown rather than just the original thrown exception? It appears that ATM exceptions thrown by metamodel invocations are just not wrapped at all, and I wonder why we should wrap them in an InvocationTargetException?

It seems to me to be a useless layer of wrapping. WDYT guys?

@chochos
Collaborator

If they need to be wrapped, it's got to be in a Ceylon exception. If you're talking about java's InvocationTargetException that's JVM-only...

@FroMage
Owner

I'm talking about our own exception type, of course.

@gavinking
Owner

I think it should just be the original exception. InvocationTargetEx is a PITA.

@thradec
Collaborator

I don't care if it is original exception or wraped into something like InvocationTargetException, but in time, when I reported it, it was wraped into java.lang.RuntimeException with message "Failed to invoke method", which was the worst case :-)

@quintesse
Collaborator

Isn't InvockationTargetException just a left-over from Java where you need it because of checked exceptions?

@gavinking
Owner

Isn't InvockationTargetException just a left-over from Java where you need it because of checked exceptions?

Not exactly; they could have made invoke() throw Exception.

But I agree it's a leftover.

@FroMage
Owner

Well it seems to just work OOTB and not produce InvocationTargetException, so perhaps you have a specific test case for me @thradec?

The tests I just did for constructors, getters, setters and methods just worked.

@tombentley
Collaborator

@FroMage isn't it the difference between invoking via MethodHandle and invoking via reflection? In other words, where in the metamodel do we use reflection?

@thradec
Collaborator

Try this please...

void run() {
    value f = `foo`;
    try {
        f();
        assert(false);
    } catch(Exception e) {
        print(e); // output is: java.lang.RuntimeException: Failed to invoke method for foo
        assert(e.message == "foo");
    }
}

void foo() {
    throw Exception("foo");
}
@FroMage
Owner

Haha, so all my tests worked because I copy/pasted the wrong test: assert(x.cause is MyException) instead of assert(x is MyException) :(

@thradec
Collaborator

:-)

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage added a private package ceylon.language.impl to be able to rethrow un…
…checked exceptions from the metamodel for #226
972d2d5
@FroMage
Owner

OK fixed rethrowing this time :)

@thradec
Collaborator

Thanks!

@FroMage FroMage referenced this issue from a commit in ceylon/ceylon-compiler
@FroMage FroMage Added langage module metamodel tests ceylon/ceylon.language#226 1fa0700
@FroMage
Owner

101 comments here and we're not even close to the 135 comments of ceylon/ceylon-spec#186, for such an important issue, can you believe it?

@FroMage
Owner

So some interesting questions about the .string attribute:

  • should it say what it points to, like class Foo or just Foo? (I think it should)
  • should it be qualified or not: Foo.attr or attr? (I think it should)
  • should we include packages in qualifiers: bla::Foo.attr or Foo.attr? (I think it should not, especially since people will likely also want the module)
  • should we visually distinguish open and closed types: class Foo&interface Gee or open Foo&Gee or Foo&Gee? (I think the first example sounds about right)
@FroMage
Owner

Interesting that Java's Class.toString() generates FQN with the class prefix: class com.redhat.ceylon.compiler.java.tools.LanguageCompiler

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: added Model.container
it's redundant with Member.declaringClassOrInterface though, but not sure how to unify the two
at least that one works even when you have bound attributes -> value which are not members anymore
ba4ade2
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: more FIXMEs bd44b3e
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: renamed ClassOrInterfaceDeclaration.superClassDeclara…
…tion and interfacesDeclaration

to extendedType and satisfiedTypes
7c2bfbc
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: renamed TopLevelOrMemberDeclaration.packageContainer …
…to containingPackage

added containingModule for half-price today special deal
d674200
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: changed type of TopLevelOrMemberDeclaration.container
to the more-specific (but erased) Package|TopLevelOrMemberDeclaration
42c851a
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: renamed type parameter bounds
to be in zen harmony with the other similar bound names diseminated by the wind
7309248
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: removed infinite loops please
funny the IDE or compiler don't catch such trivial errors
271db87
@FroMage
Owner

I think being able to get members from a Type rather than from a ClassOrInterface will wait for 1.0. Same for interop and overloading.

ATM we don't have Java's equivalent of getting all the inherited (shared) members out of a declaration, we only have the list of declarred declarations. I'm inclined to delay this to 1.0 too.

@FroMage FroMage referenced this issue from a commit in ceylon/ceylon-compiler
@FroMage FroMage ExpressionTransformer ceylon/ceylon.language#226: renamed TopLevelOrM…
…emberDeclaration to NestableDeclaration
d2c1f80
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: renamed ClassOrInterface superclass/interfaces into e…
…xtendedType/satisfiedTypes

for zen harmony
a56658f
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: turned ClassOrInterface extendedType/satisfiedType to…
… ClassModel/InterfaceModel

since they could be member types
d4d9f7d
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: fixed bug 43b54ed
@gavinking
Owner

@FroMage those limitations sound OK to me. To get members of a Type, can you ask for its supertype ClassOrInterfaces?

@FroMage
Owner

Yes.

@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: big change in apply and model containers
apply is now parameterised
memberApply too
bindAndApply is gone: you can do memberapply and invoke for the same effect
model container are not ClassOrInterface anymore but Type since we can have members with a container of union type <Foo&Bar>.f()

As a result Class adds a new memberClassApply rather than refine apply since it cannot add the required Arguments type parameter
and I did the same in Interface for regularity, though this would could refine apply, not sure yet about it
2b93133
@FroMage FroMage referenced this issue from a commit
@FroMage FroMage metamodel #226: removed callable from AppliedClass since Class is alr…
…eady Callable, and it was missing its full type from @SatisfiedTypes
47bd812
@FroMage
Owner

This issue is now officially done for the JVM :)

@quintesse
Collaborator

Gratz!

Both for the feature and for the longest issue :)

@FroMage
Owner

This is done, though not sure about the JS backend.

@FroMage FroMage closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.