Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions changelog/changelog17425.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
add __traits(getParameterStorageClasses, f, i)

$(LINK2 https://issues.dlang.org/show_bug.cgi?id=17425, Bugzilla 17425)
Copy link
Member

Choose a reason for hiding this comment

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

The changelog entry seems a bit terse. I'd use at least more descriptive variable names. Also, linking to the documentation instead of bugzilla might be more helpful.

Copy link
Contributor

Choose a reason for hiding this comment

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

FYI the auto-tester allows to preview this since some time (though I am not sure why this isn't part of the diff, CC @CyberShadow)
In any case the link is e.g. http://dtest.dlang.io/artifact/website-72ba79b73f635fb08bdfe69d6826833c1c4c2ee0-a9052bf749bf29e4ae70eb23d51613de/web/changelog/2.075.0_pre.html#changelog17425 (the _pre file in the changelog folder).
FWIW there also seems to be a parenthesis problem in another changelog file..

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't link to the documentation because that PR has not been pulled.


---
ref int foo(return ref const int* p, scope int* a, out int b, lazy int c);

pragma(msg, __traits(getParameterStorageClasses, foo, 0));

static assert(__traits(getParameterStorageClasses, foo, 0)[0] == "return");
static assert(__traits(getParameterStorageClasses, foo, 0)[1] == "ref");

pragma(msg, __traits(getParameterStorageClasses, foo, 1));
static assert(__traits(getParameterStorageClasses, foo, 1)[0] == "scope");
static assert(__traits(getParameterStorageClasses, foo, 2)[0] == "out");
static assert(__traits(getParameterStorageClasses, typeof(&foo), 3)[0] == "lazy");
---

1 change: 1 addition & 0 deletions src/ddmd/idgen.d
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ Msgtable[] msgtable =
{ "getAttributes" },
{ "getFunctionAttributes" },
{ "getFunctionVariadicStyle" },
{ "getParameterStorageClasses" },
{ "getLinkage" },
{ "getUnitTests" },
{ "getVirtualIndex" },
Expand Down
104 changes: 104 additions & 0 deletions src/ddmd/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ static this()
"getAttributes",
"getFunctionAttributes",
"getFunctionVariadicStyle",
"getParameterStorageClasses",
"getUnitTests",
"getVirtualIndex",
"getPointerBitmap",
Expand Down Expand Up @@ -968,6 +969,109 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
auto se = new StringExp(e.loc, cast(char*)style);
return se.semantic(sc);
}
if (e.ident == Id.getParameterStorageClasses)
{
/* Accept a function symbol or a type, followed by a parameter index.
Copy link
Member

Choose a reason for hiding this comment

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

How about putting this code into a function? This improves readable and avoids making this function even larger (dmd is rather slow optimizing large functions).

Copy link
Member Author

Choose a reason for hiding this comment

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

I leave refactorings to subsequent PRs, because they distract from what is being changed here.

* Returns a tuple of strings of the parameter's storage classes.
*/
// get symbol linkage as a string
if (dim != 2)
return dimError(2);

auto o1 = (*e.args)[1];
auto o = (*e.args)[0];
auto t = isType(o);
TypeFunction tf = null;
if (t)
{
if (t.ty == Tfunction)
tf = cast(TypeFunction)t;
else if (t.ty == Tdelegate)
tf = cast(TypeFunction)t.nextOf();
else if (t.ty == Tpointer && t.nextOf().ty == Tfunction)
tf = cast(TypeFunction)t.nextOf();
}
Parameters* fparams;
if (tf)
{
fparams = tf.parameters;
}
else
{
auto s = getDsymbol(o);
FuncDeclaration fd;
if (!s || (fd = s.isFuncDeclaration()) is null)
{
e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
o.toChars(), o1.toChars());
return new ErrorExp();
}
fparams = fd.getParameters(null);
}

StorageClass stc;

// Set stc to storage class of the ith parameter
auto ex = isExpression((*e.args)[1]);
if (!ex)
{
e.error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
o.toChars(), o1.toChars());
return new ErrorExp();
}
ex = ex.ctfeInterpret();
auto ii = ex.toUInteger();
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't propagate errors if the expression does not evaluate to an integer. That is not checked in other uses of toInteger/toUInteger elsewhere, too, though. Maybe add test pragma(msg, __traits(getParameterStorageClasses, foo, "1"));?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a generic problem with all those functions, and fixing them should be in a separate PR.

if (ii >= fparams.dim)
{
e.error("parameter index must be in range 0..%u not %s", cast(uint)fparams.dim, ex.toChars());
return new ErrorExp();
}

uint n = cast(uint)ii;
Parameter p = Parameter.getNth(fparams, n);
stc = p.storageClass;

// This mirrors hdrgen.visit(Parameter p)
if (p.type && p.type.mod & MODshared)
stc &= ~STCshared;

auto exps = new Expressions;

void push(string s)
{
exps.push(new StringExp(e.loc, cast(char*)s.ptr, cast(uint)s.length));
}

if (stc & STCauto)
push("auto");
if (stc & STCreturn)
push("return");

if (stc & STCout)
push("out");
else if (stc & STCref)
push("ref");
else if (stc & STCin)
push("in");
else if (stc & STClazy)
push("lazy");
else if (stc & STCalias)
push("alias");

if (stc & STCconst)
push("const");
if (stc & STCimmutable)
push("immutable");
if (stc & STCwild)
push("inout");
if (stc & STCshared)
push("shared");
if (stc & STCscope)
push("scope");

auto tup = new TupleExp(e.loc, exps);
return tup.semantic(sc);
}
if (e.ident == Id.getLinkage)
{
// get symbol linkage as a string
Expand Down
32 changes: 32 additions & 0 deletions test/fail_compilation/test17425.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* TEST_OUTPUT:
---
fail_compilation/test17425.d(24): Error: parameter index must be in range 0..4 not 4
fail_compilation/test17425.d(27): Error: first argument to `__traits(getParameterStorageClasses, i, 4)` is not a function
fail_compilation/test17425.d(29): Error: expression expected as second argument of `__traits(getParameterStorageClasses, foo, int)`
fail_compilation/test17425.d(31): Error: expected 2 arguments for `getParameterStorageClasses` but had 3
---
*/

// https://issues.dlang.org/show_bug.cgi?id=17425

ref int foo(return ref const int* p, scope int* a, out int b, lazy int c);

//pragma(msg, __traits(getParameterStorageClasses, foo, 0));

static assert(__traits(getParameterStorageClasses, foo, 0)[0] == "return");
static assert(__traits(getParameterStorageClasses, foo, 0)[1] == "ref");

//pragma(msg, __traits(getParameterStorageClasses, foo, 1));
static assert(__traits(getParameterStorageClasses, foo, 1)[0] == "scope");
static assert(__traits(getParameterStorageClasses, foo, 2)[0] == "out");
static assert(__traits(getParameterStorageClasses, typeof(&foo), 3)[0] == "lazy");

enum a1 = __traits(getParameterStorageClasses, foo, 4);

int i;
enum a2 = __traits(getParameterStorageClasses, i, 4);

enum a3 = __traits(getParameterStorageClasses, foo, int);

enum a4 = __traits(getParameterStorageClasses, foo, 0, 1);