Skip to content
Merged
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
81 changes: 51 additions & 30 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -1000,8 +1000,11 @@ template arity(alias func)
}

/**
Returns a tuple consisting of the storage classes of the parameters of a
function $(D func).
Get tuple, one per function parameter, of the storage classes of the parameters.
Params:
func = function symbol or type of function, delegate, or pointer to function
Returns:
a tuple of ParameterStorageClass bits
*/
enum ParameterStorageClass : uint
{
Expand All @@ -1021,39 +1024,28 @@ enum ParameterStorageClass : uint
template ParameterStorageClassTuple(func...)
if (func.length == 1 && isCallable!func)
{
alias Func = Unqual!(FunctionTypeOf!func);
alias Func = FunctionTypeOf!func;

/*
* TypeFuncion:
* CallConvention FuncAttrs Arguments ArgClose Type
*/
alias Params = Parameters!Func;

// chop off CallConvention and FuncAttrs
enum margs = demangleFunctionAttributes(mangledName!Func[1 .. $]).rest;

// demangle Arguments and store parameter storage classes in a tuple
template demangleNextParameter(string margs, size_t i = 0)
static if (is(Func PT == __parameters))
{
static if (i < Params.length)
{
enum demang = demangleParameterStorageClass(margs);
enum skip = mangledName!(Params[i]).length; // for bypassing Type
enum rest = demang.rest;

alias demangleNextParameter =
TypeTuple!(
demang.value + 0, // workaround: "not evaluatable at ..."
demangleNextParameter!(rest[skip .. $], i + 1)
);
}
else // went thru all the parameters
template StorageClass(size_t i)
{
alias demangleNextParameter = TypeTuple!();
static if (i < PT.length)
{
alias StorageClass = TypeTuple!(
extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i)),
StorageClass!(i + 1));
}
else
alias StorageClass = TypeTuple!();
}
alias ParameterStorageClassTuple = StorageClass!0;
}
else
{
static assert(0, func[0].stringof ~ " is not a function");
alias ParameterStorageClassTuple = TypeTuple!();
}

alias ParameterStorageClassTuple = demangleNextParameter!margs;
}

///
Expand All @@ -1071,6 +1063,35 @@ template ParameterStorageClassTuple(func...)
static assert(pstc[2] == STC.none);
}

/*****************
* Convert string tuple Attribs to ParameterStorageClass bits
* Params:
* Attribs = string tuple
* Returns:
* ParameterStorageClass bits
*/
ParameterStorageClass extractParameterStorageClassFlags(Attribs...)()
{
auto result = ParameterStorageClass.none;
foreach (attrib; Attribs)
{
final switch (attrib) with (ParameterStorageClass)
{
case "scope": result |= scope_; break;
case "out": result |= out_; break;
case "ref": result |= ref_; break;
case "lazy": result |= lazy_; break;
case "return": result |= return_; break;
}
}
/* Mimic behavor of original version of ParameterStorageClassTuple()
* to avoid breaking existing code.
*/
if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_))
result = ParameterStorageClass.return_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this really a concern as the result is passed via bit flags?
Isn't a much bigger concern that ParameterStorageClassTuple will then for this subset return an invalid/unexpected result and thus potentially break much more code in the future?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This function is used extensively by many organizations, and breaking legacy behavior is beyond the scope of this PR.

return result;
}

@safe unittest
{
alias STC = ParameterStorageClass;
Expand Down