Skip to content

New __traits: isModule and isPackage#5290

Merged
dlang-bot merged 1 commit intodlang:masterfrom
blm768:new_traits
May 19, 2019
Merged

New __traits: isModule and isPackage#5290
dlang-bot merged 1 commit intodlang:masterfrom
blm768:new_traits

Conversation

@blm768
Copy link
Contributor

@blm768 blm768 commented Dec 1, 2015

This pull request would add two new __traits: isModule and isPackage, which (unsurprisingly) allow one to determine whether a symbol represents a module and/or a package. These traits can be quite useful for introspection under certain circumstances, especially some types of code generation (i.e. bindings).

Spec change: dlang/dlang.org#2199

src/traits.d Outdated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use Phobos utilities in dmd code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. What does DMD normally use for string concatenation? Should I just use strcat?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printf followed by assert(0), inside an if.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should work. I'll probably use fprintf(stderr, ...), though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to follow the existing style, which prints to stdout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. That's a little odd. I guess it works, though. Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We try not to import phobos inside the dmd test suite either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking for something that had some "plain" packages plus modules and "package.d", but I suppose a cleaner solution would be to make a module in test/runnable/imports. I'll fix that.

src/traits.d Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error("Internal Compiler Error: your message %s", imp.toChars()); ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That might work. I thought the error() function needed a Loc object, though. I'm not sure how to get one in this context.

Edit: oh, duh. e.loc should work. I think...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just Loc() for no location.

@blm768
Copy link
Contributor Author

blm768 commented Dec 10, 2015

I may have rebased this a bit prematurely, but it's rebased to master now.

@andralex
Copy link
Member

Looks meaningful. @WalterBright ?

@blm768
Copy link
Contributor Author

blm768 commented Feb 12, 2016

Fixed the merge conflict and rebased again.

@blm768
Copy link
Contributor Author

blm768 commented Mar 23, 2016

Is approval by @WalterBright the only thing holding this PR back? There's no rush; I just want to make sure there's nothing I need to fix. (Well, and bump this PR so it doesn't get buried forever ;)...)

@AndrejMitrovic
Copy link
Contributor

These traits can be quite useful for introspection under certain circumstances, especially some types of code generation (i.e. bindings).

Could you show some real-world use-cases? Also, an associated bugzilla entry should be linked to (create one if it doesn't exist). Thanks.

@blm768
Copy link
Contributor Author

blm768 commented May 7, 2016

Could you show some real-world use-cases? Also, an associated bugzilla entry should be linked to (create one if it doesn't exist). Thanks.

The use case that prompted this was the refactoring of my old Bullet Physics bindings. Originally, it used a kludgy build process that looped over the source files and created a new D program that would iterate over all the packages and pull out relevant symbols that needed "glue" code to interact with C++. Being able to iterate over the packages directly at compile time would be a fair bit cleaner. With the improved C++ interop that has developed since I started this PR, it's probably not as relevant as it used to be, but I can see it still being useful for binding to other languages.

I'll create a Bugzilla entry. Thanks for pointing that out.

EDIT: here's the Bugzilla entry.

@AndrejMitrovic
Copy link
Contributor

Pretty solid use request! :)

I'll take a look in the front-end to see if we can use a hashmap of some kind to avoid O(n) lookups.

@blm768
Copy link
Contributor Author

blm768 commented May 17, 2016

I'll take a look in the front-end to see if we can use a hashmap of some kind to avoid O(n) lookups.

So basically a __traits(allPackages)?

@AndrejMitrovic
Copy link
Contributor

Sorry, I thought you introduced a linear lookup somewhere, I might have been wrong here.

@blm768
Copy link
Contributor Author

blm768 commented May 17, 2016

Sorry, I thought you introduced a linear lookup somewhere, I might have been wrong here.

It should mostly be constant-time in this code. resolvPkgUnknown is linear with respect to the nesting depth of the package when it builds the identifier chain to try to find a package.d, but that's dwarfed by the overhead of the filesystem access that it does. Any iteration would be done by the user of the traits, who would (at least under certain circumstances) iterate over symbols and figure out which ones are packages.

@AndrejMitrovic
Copy link
Contributor

Anyway I don't find this is a feature that needs a whole lot of discussion, it's pretty simple. Could you rebase it against master?

@blm768
Copy link
Contributor Author

blm768 commented May 18, 2016

Done. Now we'll see if the auto-tester likes it...

@AndrejMitrovic
Copy link
Contributor

@WalterBright @andralex: this is a pretty low-impact feature request, can we get an a-ok for adding the two new traits? It would benefit the BulletD project (which is high on the list of wanted C++ bindings, right up there with QtD).

@AndrejMitrovic
Copy link
Contributor

@blm768: P.S. you're going to have to squash your commits. :)

But let's wait for W&A's approval first and then I'll have another look at the code and merge it.

@lodo1995
Copy link

What's the status of this? It would be very useful.
For example, I'm currently trying to write some code that looks for all available templates marked with a specific UDA. To do this, I need to enumerate all visible symbols and if a symbol is an imported module or package I need to enumerate its members. These two traits would be very useful in this case.

@blm768 blm768 force-pushed the new_traits branch 2 times, most recently from 34e275c to bd60620 Compare July 23, 2016 18:01
@blm768
Copy link
Contributor Author

blm768 commented Jul 23, 2016

Just rebased again and squashed.

@blm768 blm768 force-pushed the new_traits branch 3 times, most recently from ab065f4 to 6159360 Compare March 10, 2019 19:40
@blm768
Copy link
Contributor Author

blm768 commented Mar 16, 2019

Managed to find and fix another off-by-one error in my changes. Apparently my D is rusty or something.

Anyway, it looks like something in my changes has broken some aspect of scope resolution inside the is() expression or something. The following minimal example is producing an error:

module test.compilable.test16002;
import imports.plainpackage.plainmodule;
static assert(!is(imports.plainpackage.plainmodule == package));
Error: module `imports.plainpackage.plainmodule` from file test/compilable/imports/plainpackage/plainmodule.d must be imported with 'import imports.plainpackage.plainmodule;'
test/compilable/test16002.d(13):        while evaluating: static assert(!false)

I haven't managed to determine why yet, but what seems to be happening is that the result of e.targ.toDsymbol is a Package with isPkgMod == PKG.unknown (even though it's just a plain module inside a package), which is causing a call to resolvePKGUnknown, which locates the correct filename but then raises the error message in Module.load. It doesn't seem to make any difference whether I use the original Scope object (sc) or the modified one that the IsExp visitor uses for types (sc2)

@thewilsonator
Copy link
Contributor

It was decided to have both.

@blm768
Copy link
Contributor Author

blm768 commented May 11, 2019

@thewilsonator: Both the __traits and is()? Or are you referring to something related to PGK.unknown resolution?

@thewilsonator
Copy link
Contributor

Both __traits and is()

@thewilsonator
Copy link
Contributor

(I'm about to travel back home so it will be probably 2 days before I get back around to this. Do ping me if I forget!)

@blm768 blm768 force-pushed the new_traits branch 3 times, most recently from 6a803ec to 44c6685 Compare May 18, 2019 06:47
@thewilsonator
Copy link
Contributor

Thanks, please squash the commits and this is good to go.

@thewilsonator
Copy link
Contributor

==============================
Test fail_compilation/traits.d failed: 
expected:
----
fail_compilation/traits.d(100): Error: `getTargetInfo` key `"not_a_target_info"` not supported by this implementation
fail_compilation/traits.d(101): Error: string expected as argument of __traits `getTargetInfo` instead of `100`
fail_compilation/traits.d(102): Error: expected 1 arguments for `getTargetInfo` but had 2
fail_compilation/traits.d(103): Error: expected 1 arguments for `getTargetInfo` but had 0
fail_compilation/traits.d(200): Error: undefined identifier `imports.nonexistent`
fail_compilation/traits.d(201): Error: undefined identifier `imports.nonexistent`
fail_compilation/traits.d(200): Error: undefined identifier imports.nonexistent
fail_compilation/traits.d(201): Error: undefined identifier imports.nonexistent
fail_compilation/traits.d(202): Error: expected 1 arguments for `isPackage` but had 0
fail_compilation/traits.d(203): Error: expected 1 arguments for `isModule` but had 0
----
actual:
----
fail_compilation/traits.d(100): Error: `getTargetInfo` key `"not_a_target_info"` not supported by this implementation
fail_compilation/traits.d(101): Error: string expected as argument of __traits `getTargetInfo` instead of `100`
fail_compilation/traits.d(102): Error: expected 1 arguments for `getTargetInfo` but had 2
fail_compilation/traits.d(103): Error: expected 1 arguments for `getTargetInfo` but had 0
fail_compilation/traits.d(200): Error: undefined identifier `imports.nonexistent`
fail_compilation/traits.d(201): Error: undefined identifier `imports.nonexistent`
fail_compilation/traits.d(202): Error: expected 1 arguments for `isPackage` but had 0
fail_compilation/traits.d(203): Error: expected 1 arguments for `isModule` but had 0
----

@blm768
Copy link
Contributor Author

blm768 commented May 19, 2019

==============================
Test fail_compilation/traits.d failed: 
...

Yep. With my current level of consciousness, that was about guaranteed to happen. I really shouldn't be allowed to code at this time of night.

@thewilsonator
Copy link
Contributor

dmd/expressionsem.d(2321): Warning: Ddoc: parameter count mismatch, expected 1, got 0
dmd/expressionsem.d(2321):        Note that the format is `param = description`

@thewilsonator
Copy link
Contributor

Test fail_compilation/test16002.d failed: 
expected:
----
fail_compilation/test16002.d(100): Error: undefined identifier `imports.nonexistent`
fail_compilation/test16002.d(100):        while evaluating: `static assert(is(imports.nonexistent == package))`
fail_compilation/test16002.d(101): Error: undefined identifier `imports.nonexistent`
fail_compilation/test16002.d(101):        while evaluating: `static assert(is(imports.nonexistent == module))`
----
actual:
----
fail_compilation/test16002.d(100): Error: undefined identifier `imports.nonexistent`
fail_compilation/test16002.d(101): Error: undefined identifier `imports.nonexistent`
----

@blm768
Copy link
Contributor Author

blm768 commented May 19, 2019

Sorry; I'm really hammering those poor CI servers with all these failed attempts. Thanks for your patience while I fumble through this.

@thewilsonator
Copy link
Contributor

No problems, I hammer them frequently too. That you for your continued interest!

@blm768
Copy link
Contributor Author

blm768 commented May 19, 2019

Well, I figured I might as well get this finished after three years. ;)

I'll try to get the changes to the spec hammered out in the next couple of days.

@thewilsonator
Copy link
Contributor

Haha, thanks.

@dlang-bot dlang-bot merged commit d6158b4 into dlang:master May 19, 2019
@pbackus pbackus mentioned this pull request Mar 30, 2022
@ntrel
Copy link
Contributor

ntrel commented Feb 25, 2023

@thewilsonator

It was decided to have both.
Both __traits and is()

Why was that? is should only be true when its argument is a valid type - this is important conceptually. A module is not a type. I think we should deprecate is(m == module), is(p == package) for this reason.

@thewilsonator
Copy link
Contributor

I don't remember, this was 4 years ago.

I think we should deprecate is(m == module), is(p == package) for this reason.
I don't think so, is(foo == keyword) is different from is(foo == type).
There is no practical utility in deprecating it: it can have no other meaning, it does not compete for design space with anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Comments