mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-19 15:11:08 +08:00
d: Merge upstream dmd 0450061c8
D front-end changes: - Fix ICE in forward referenced type members of structs. - Fix ICE passing a member template mixin identifier as alias argument. - Fix ICE when `__traits' prints error involving a Parameter. - Fix bugs found in `__traits(allMembers)' returning wrong result. - Detect and shortcut Alias and AliasSeq template patterns. Reviewed-on: https://github.com/dlang/dmd/pull/12405 https://github.com/dlang/dmd/pull/12411 gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 0450061c8.
This commit is contained in:
parent
d118ec221d
commit
0344b5b822
@ -1,4 +1,4 @@
|
||||
d16195406e1795ee91f2acb8f522fcb4ec698f47
|
||||
0450061c8de71328815da9323bd35c92b37d51d2
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the dlang/dmd repository.
|
||||
|
@ -260,6 +260,8 @@ void AggregateDeclaration::setScope(Scope *sc)
|
||||
*/
|
||||
bool AggregateDeclaration::determineFields()
|
||||
{
|
||||
if (_scope)
|
||||
dsymbolSemantic(this, NULL);
|
||||
if (sizeok != SIZEOKnone)
|
||||
return true;
|
||||
|
||||
|
@ -266,6 +266,8 @@ public:
|
||||
virtual UnitTestDeclaration *isUnitTestDeclaration() { return NULL; }
|
||||
virtual NewDeclaration *isNewDeclaration() { return NULL; }
|
||||
virtual VarDeclaration *isVarDeclaration() { return NULL; }
|
||||
virtual VersionSymbol *isVersionSymbol() { return NULL; }
|
||||
virtual DebugSymbol *isDebugSymbol() { return NULL; }
|
||||
virtual ClassDeclaration *isClassDeclaration() { return NULL; }
|
||||
virtual StructDeclaration *isStructDeclaration() { return NULL; }
|
||||
virtual UnionDeclaration *isUnionDeclaration() { return NULL; }
|
||||
|
@ -570,7 +570,7 @@ public:
|
||||
ti = dsym->_init ? dsym->_init->syntaxCopy() : NULL;
|
||||
|
||||
VarDeclaration *v = new VarDeclaration(dsym->loc, arg->type, id, ti);
|
||||
v->storage_class |= STCtemp | dsym->storage_class;
|
||||
v->storage_class |= STCtemp | STClocal | dsym->storage_class;
|
||||
if (arg->storageClass & STCparameter)
|
||||
v->storage_class |= arg->storageClass;
|
||||
//printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars());
|
||||
@ -4843,6 +4843,54 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************
|
||||
* Do template instance semantic for isAliasSeq templates.
|
||||
* This is a greatly simplified version of TemplateInstance::semantic().
|
||||
*/
|
||||
static void aliasSeqInstanceSemantic(TemplateInstance *tempinst, Scope *sc, TemplateDeclaration *tempdecl)
|
||||
{
|
||||
//printf("[%s] aliasSeqInstanceSemantic('%s')\n", tempinst->loc.toChars(), tempinst->toChars());
|
||||
Scope *paramscope = sc->push();
|
||||
paramscope->stc = 0;
|
||||
paramscope->protection = Prot(Prot::public_);
|
||||
|
||||
TemplateTupleParameter *ttp = (*tempdecl->parameters)[0]->isTemplateTupleParameter();
|
||||
Tuple *va = isTuple(tempinst->tdtypes[0]);
|
||||
Declaration *d = new TupleDeclaration(tempinst->loc, ttp->ident, &va->objects);
|
||||
d->storage_class |= STCtemplateparameter;
|
||||
dsymbolSemantic(d, sc);
|
||||
|
||||
paramscope->pop();
|
||||
|
||||
tempinst->aliasdecl = d;
|
||||
|
||||
tempinst->semanticRun = PASSsemanticdone;
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Do template instance semantic for isAlias templates.
|
||||
* This is a greatly simplified version of TemplateInstance::semantic().
|
||||
*/
|
||||
static void aliasInstanceSemantic(TemplateInstance *tempinst, Scope *sc, TemplateDeclaration *tempdecl)
|
||||
{
|
||||
//printf("[%s] aliasInstanceSemantic('%s')\n", tempinst->loc.toChars(), tempinst->toChars());
|
||||
Scope *paramscope = sc->push();
|
||||
paramscope->stc = 0;
|
||||
paramscope->protection = Prot(Prot::public_);
|
||||
|
||||
TemplateTypeParameter *ttp = (*tempdecl->parameters)[0]->isTemplateTypeParameter();
|
||||
Type *ta = isType(tempinst->tdtypes[0]);
|
||||
Declaration *d = new AliasDeclaration(tempinst->loc, ttp->ident, ta->addMod(tempdecl->onemember->isAliasDeclaration()->type->mod));
|
||||
d->storage_class |= STCtemplateparameter;
|
||||
dsymbolSemantic(d, sc);
|
||||
|
||||
paramscope->pop();
|
||||
|
||||
tempinst->aliasdecl = d;
|
||||
|
||||
tempinst->semanticRun = PASSsemanticdone;
|
||||
}
|
||||
|
||||
void templateInstanceSemantic(TemplateInstance *tempinst, Scope *sc, Expressions *fargs)
|
||||
{
|
||||
//printf("[%s] TemplateInstance::semantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst->loc.toChars(), tempinst->toChars(), tempinst, global.gag, sc);
|
||||
@ -4913,6 +4961,21 @@ Lerror:
|
||||
if (tempinst->errors)
|
||||
goto Lerror;
|
||||
|
||||
/* Greatly simplified semantic processing for AliasSeq templates
|
||||
*/
|
||||
if (tempdecl->isTrivialAliasSeq)
|
||||
{
|
||||
tempinst->inst = tempinst;
|
||||
return aliasSeqInstanceSemantic(tempinst, sc, tempdecl);
|
||||
}
|
||||
/* Greatly simplified semantic processing for Alias templates
|
||||
*/
|
||||
else if (tempdecl->isTrivialAlias)
|
||||
{
|
||||
tempinst->inst = tempinst;
|
||||
return aliasInstanceSemantic(tempinst, sc, tempdecl);
|
||||
}
|
||||
|
||||
/* See if there is an existing TemplateInstantiation that already
|
||||
* implements the typeargs. If so, just refer to that one instead.
|
||||
*/
|
||||
|
@ -222,7 +222,8 @@ bool definitelyValueParameter(Expression *e)
|
||||
e->op == TOKtype || e->op == TOKdottype ||
|
||||
e->op == TOKtemplate || e->op == TOKdottd ||
|
||||
e->op == TOKfunction || e->op == TOKerror ||
|
||||
e->op == TOKthis || e->op == TOKsuper)
|
||||
e->op == TOKthis || e->op == TOKsuper ||
|
||||
e->op == TOKdot)
|
||||
return false;
|
||||
|
||||
if (e->op != TOKdotvar)
|
||||
@ -531,6 +532,8 @@ TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id,
|
||||
this->literal = literal;
|
||||
this->ismixin = ismixin;
|
||||
this->isstatic = true;
|
||||
this->isTrivialAliasSeq = false;
|
||||
this->isTrivialAlias = false;
|
||||
this->previous = NULL;
|
||||
this->protection = Prot(Prot::undefined);
|
||||
this->inuse = 0;
|
||||
@ -538,13 +541,46 @@ TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id,
|
||||
|
||||
// Compute in advance for Ddoc's use
|
||||
// Bugzilla 11153: ident could be NULL if parsing fails.
|
||||
if (members && ident)
|
||||
if (!members || !ident)
|
||||
return;
|
||||
|
||||
Dsymbol *s;
|
||||
if (!Dsymbol::oneMembers(members, &s, ident) || !s)
|
||||
return;
|
||||
|
||||
onemember = s;
|
||||
s->parent = this;
|
||||
|
||||
/* Set isTrivialAliasSeq if this fits the pattern:
|
||||
* template AliasSeq(T...) { alias AliasSeq = T; }
|
||||
* or set isTrivialAlias if this fits the pattern:
|
||||
* template Alias(T) { alias Alias = qualifiers(T); }
|
||||
*/
|
||||
if (!(parameters && parameters->length == 1))
|
||||
return;
|
||||
|
||||
AliasDeclaration *ad = s->isAliasDeclaration();
|
||||
if (!ad || !ad->type)
|
||||
return;
|
||||
|
||||
TypeIdentifier *ti = ad->type->isTypeIdentifier();
|
||||
if (!ti || ti->idents.length != 0)
|
||||
return;
|
||||
|
||||
if (TemplateTupleParameter *ttp = (*parameters)[0]->isTemplateTupleParameter())
|
||||
{
|
||||
Dsymbol *s;
|
||||
if (Dsymbol::oneMembers(members, &s, ident) && s)
|
||||
if (ti->ident == ttp->ident && ti->mod == 0)
|
||||
{
|
||||
onemember = s;
|
||||
s->parent = this;
|
||||
//printf("found isAliasSeq %s %s\n", s->toChars(), ad->type->toChars());
|
||||
isTrivialAliasSeq = true;
|
||||
}
|
||||
}
|
||||
else if (TemplateTypeParameter *ttp = (*parameters)[0]->isTemplateTypeParameter())
|
||||
{
|
||||
if (ti->ident == ttp->ident)
|
||||
{
|
||||
//printf("found isAlias %s %s\n", s->toChars(), ad->type->toChars());
|
||||
isTrivialAlias = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6223,6 +6259,14 @@ bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f
|
||||
sa = ((DotTemplateExp *)ea)->td;
|
||||
goto Ldsym;
|
||||
}
|
||||
if (ea->op == TOKdot)
|
||||
{
|
||||
if (ScopeExp *se = ((DotExp *)ea)->e2->isScopeExp())
|
||||
{
|
||||
sa = se->sds;
|
||||
goto Ldsym;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sa)
|
||||
{
|
||||
@ -6770,8 +6814,7 @@ Dsymbols *TemplateInstance::appendToModuleMember()
|
||||
{
|
||||
Module *mi = minst; // instantiated -> inserted module
|
||||
|
||||
if (global.params.useUnitTests ||
|
||||
global.params.debuglevel)
|
||||
if (global.params.useUnitTests)
|
||||
{
|
||||
// Turn all non-root instances to speculative
|
||||
if (mi && !mi->isRoot())
|
||||
|
@ -1714,6 +1714,10 @@ public:
|
||||
objectToBuffer(arg);
|
||||
}
|
||||
}
|
||||
else if (Parameter *p = isParameter(oarg))
|
||||
{
|
||||
p->accept(this);
|
||||
}
|
||||
else if (!oarg)
|
||||
{
|
||||
buf->writestring("NULL");
|
||||
|
@ -7039,7 +7039,10 @@ Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int
|
||||
*/
|
||||
e = expressionSemantic(e, sc); // do this before turning on noaccesscheck
|
||||
|
||||
sym->size(e->loc); // do semantic of type
|
||||
if (!sym->determineFields())
|
||||
{
|
||||
error(e->loc, "unable to determine fields of `%s` because of forward references", toChars());
|
||||
}
|
||||
|
||||
Expression *e0 = NULL;
|
||||
Expression *ev = e->op == TOKtype ? NULL : e;
|
||||
@ -7373,7 +7376,9 @@ bool TypeStruct::hasPointers()
|
||||
// Probably should cache this information in sym rather than recompute
|
||||
StructDeclaration *s = sym;
|
||||
|
||||
sym->size(Loc()); // give error for forward references
|
||||
if (sym->members && !sym->determineFields() && sym->type != Type::terror)
|
||||
error(sym->loc, "no size because of forward references");
|
||||
|
||||
for (size_t i = 0; i < s->fields.length; i++)
|
||||
{
|
||||
Declaration *d = s->fields[i];
|
||||
|
@ -77,6 +77,8 @@ public:
|
||||
bool literal; // this template declaration is a literal
|
||||
bool ismixin; // template declaration is only to be used as a mixin
|
||||
bool isstatic; // this is static template declaration
|
||||
bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; }
|
||||
bool isTrivialAlias; // matches `template Alias(T) { alias Alias = T; }
|
||||
Prot protection;
|
||||
int inuse; // for recursive expansion detection
|
||||
|
||||
|
@ -1560,6 +1560,13 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
|
||||
s = imp->mod;
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=16044
|
||||
if (Package *p = s->isPackage())
|
||||
{
|
||||
if (Module *pm = p->isPackageMod())
|
||||
s = pm;
|
||||
}
|
||||
|
||||
ScopeDsymbol *sds = s->isScopeDsymbol();
|
||||
if (!sds || sds->isTemplateDeclaration())
|
||||
{
|
||||
@ -1587,6 +1594,11 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
|
||||
}
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=20915
|
||||
// skip version and debug identifiers
|
||||
if (sm->isVersionSymbol() || sm->isDebugSymbol())
|
||||
return 0;
|
||||
|
||||
//printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars());
|
||||
if (sm->ident)
|
||||
{
|
||||
|
@ -797,7 +797,7 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc)
|
||||
|
||||
if (tf->isreturn && !tf->isref && !tf->next->hasPointers())
|
||||
{
|
||||
::error(loc, "function type `%s` has `return` but does not return any indirections", tf->toChars());
|
||||
tf->isreturn = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -872,7 +872,7 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc)
|
||||
if (0 && !tf->isref)
|
||||
{
|
||||
StorageClass stc = fparam->storageClass & (STCref | STCout);
|
||||
::error(loc, "parameter %s is `return %s` but function does not return by ref",
|
||||
::error(loc, "parameter `%s` is `return %s` but function does not return by `ref`",
|
||||
fparam->ident ? fparam->ident->toChars() : "",
|
||||
stcToChars(stc));
|
||||
errors = true;
|
||||
@ -886,9 +886,7 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc)
|
||||
}
|
||||
else if (!tf->isref && tf->next && !tf->next->hasPointers())
|
||||
{
|
||||
::error(loc, "parameter %s is `return` but function does not return any indirections",
|
||||
fparam->ident ? fparam->ident->toChars() : "");
|
||||
errors = true;
|
||||
fparam->storageClass &= STCreturn; // https://issues.dlang.org/show_bug.cgi?id=18963
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ public:
|
||||
const char *toChars();
|
||||
void addMember(Scope *sc, ScopeDsymbol *sds);
|
||||
const char *kind() const;
|
||||
DebugSymbol *isDebugSymbol() { return this; }
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
};
|
||||
|
||||
@ -39,5 +40,6 @@ public:
|
||||
const char *toChars();
|
||||
void addMember(Scope *sc, ScopeDsymbol *sds);
|
||||
const char *kind() const;
|
||||
VersionSymbol *isVersionSymbol() { return this; }
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
};
|
||||
|
@ -0,0 +1,4 @@
|
||||
module pkg16044;
|
||||
|
||||
int test1;
|
||||
int test2;
|
@ -0,0 +1,4 @@
|
||||
module pkg16044.sub;
|
||||
|
||||
int test3;
|
||||
int test4;
|
9
gcc/testsuite/gdc.test/compilable/issue16044.d
Normal file
9
gcc/testsuite/gdc.test/compilable/issue16044.d
Normal file
@ -0,0 +1,9 @@
|
||||
// REQUIRED_ARGS: -Icompilable/imports
|
||||
// EXTRA_FILES: imports/pkg16044/package.d imports/pkg16044/sub/package.d
|
||||
module issue16044; // https://issues.dlang.org/show_bug.cgi?id=16044
|
||||
|
||||
import pkg16044;
|
||||
import pkg16044.sub;
|
||||
|
||||
static assert([__traits(allMembers, pkg16044)] == ["object", "test1", "test2"]);
|
||||
static assert([__traits(allMembers, pkg16044.sub)] == ["object", "test3", "test4"]);
|
10
gcc/testsuite/gdc.test/compilable/issue20915.d
Normal file
10
gcc/testsuite/gdc.test/compilable/issue20915.d
Normal file
@ -0,0 +1,10 @@
|
||||
module issue20915;
|
||||
|
||||
// prior to the PR adding this test case,
|
||||
// locally defined version and debug idents were included.
|
||||
version = illegal;
|
||||
debug = illegal;
|
||||
|
||||
alias Seq(T...) = T;
|
||||
|
||||
static assert (__traits(allMembers, issue20915) == Seq!("object", "Seq"));
|
13
gcc/testsuite/gdc.test/compilable/issue21813a.d
Normal file
13
gcc/testsuite/gdc.test/compilable/issue21813a.d
Normal file
@ -0,0 +1,13 @@
|
||||
// https://issues.dlang.org/show_bug.cgi?id=21813
|
||||
Target.OS defaultTargetOS()
|
||||
{
|
||||
return Target.OS.linux;
|
||||
}
|
||||
|
||||
struct Target
|
||||
{
|
||||
enum OS { linux }
|
||||
OS os = defaultTargetOS();
|
||||
void deinitialize() { this = this.init; }
|
||||
@property isPOSIX() scope @nogc { }
|
||||
}
|
13
gcc/testsuite/gdc.test/compilable/issue21813b.d
Normal file
13
gcc/testsuite/gdc.test/compilable/issue21813b.d
Normal file
@ -0,0 +1,13 @@
|
||||
// https://issues.dlang.org/show_bug.cgi?id=21813
|
||||
Target.OS defaultTargetOS()
|
||||
{
|
||||
return Target.OS.linux;
|
||||
}
|
||||
|
||||
struct Target
|
||||
{
|
||||
enum OS { linux }
|
||||
OS os = defaultTargetOS();
|
||||
@property isPOSIX() scope @nogc { }
|
||||
}
|
||||
|
14
gcc/testsuite/gdc.test/compilable/test19145.d
Normal file
14
gcc/testsuite/gdc.test/compilable/test19145.d
Normal file
@ -0,0 +1,14 @@
|
||||
// https://issues.dlang.org/show_bug.cgi?id=19415
|
||||
|
||||
struct S
|
||||
{
|
||||
int x;
|
||||
S foo() return { return S(x); }
|
||||
this(this) @disable;
|
||||
}
|
||||
|
||||
S bar()
|
||||
{
|
||||
S s;
|
||||
return s; // Error: struct `S` is not copyable because it is annotated with @disable
|
||||
}
|
46
gcc/testsuite/gdc.test/compilable/test20894.d
Normal file
46
gcc/testsuite/gdc.test/compilable/test20894.d
Normal file
@ -0,0 +1,46 @@
|
||||
// https://issues.dlang.org/show_bug.cgi?id=20894
|
||||
|
||||
mixin template MT()
|
||||
{
|
||||
int a;
|
||||
alias b = char;
|
||||
void c() {}
|
||||
}
|
||||
|
||||
struct S
|
||||
{
|
||||
mixin MT mt;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
auto r = S();
|
||||
enum c = S();
|
||||
|
||||
foo!(S.mt);
|
||||
foo!(r.mt);
|
||||
foo!(c.mt); // OK <- ICE
|
||||
|
||||
foo!(mixin("S.mt"));
|
||||
foo!(mixin("r.mt")); // OK <- ICE
|
||||
foo!(mixin("c.mt")); // OK <- ICE
|
||||
|
||||
// some checks
|
||||
foo!(r.mt, c.mt);
|
||||
foo!(mixin("r.mt"), c.mt);
|
||||
foo!(r.mt, mixin("c.mt"));
|
||||
foo!(S.mt, mixin("c.mt"));
|
||||
}
|
||||
|
||||
alias Tup(T...) = T;
|
||||
|
||||
void foo(A...)()
|
||||
{
|
||||
static if (A.length == 2)
|
||||
{
|
||||
static assert(__traits(isSame, A[0], A[1]));
|
||||
enum members = __traits(allMembers, A[0]);
|
||||
static assert(members == __traits(allMembers, A[1]));
|
||||
static assert(members == Tup!("a", "b", "c"));
|
||||
}
|
||||
}
|
10
gcc/testsuite/gdc.test/compilable/test21812.d
Normal file
10
gcc/testsuite/gdc.test/compilable/test21812.d
Normal file
@ -0,0 +1,10 @@
|
||||
// https://issues.dlang.org/show_bug.cgi?id=21812
|
||||
|
||||
struct S(A...)
|
||||
{
|
||||
A args;
|
||||
}
|
||||
|
||||
static assert(__traits(allMembers, S!(int, float)) == AliasSeq!("args"));
|
||||
|
||||
alias AliasSeq(T...) = T;
|
16
gcc/testsuite/gdc.test/fail_compilation/diag19196.d
Normal file
16
gcc/testsuite/gdc.test/fail_compilation/diag19196.d
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/diag19196.d(11): Error: unable to determine fields of `B` because of forward references
|
||||
fail_compilation/diag19196.d(15): Error: template instance `diag19196.Foo!(B)` error instantiating
|
||||
---
|
||||
*/
|
||||
module diag19196;
|
||||
struct Foo(T)
|
||||
{
|
||||
alias F = typeof(T.tupleof);
|
||||
}
|
||||
struct B
|
||||
{
|
||||
Foo!B b;
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
/* REQUIRED_ARGS: -dip25
|
||||
TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/test16228.d(22): Error: function type 'return int()' has 'return' but does not return any indirections
|
||||
fail_compilation/test16228.d(23): Error: function test16228.S.foo static member has no 'this' to which 'return' can apply
|
||||
fail_compilation/test16228.d(23): Error: function `test16228.S.bar` `static` member has no `this` to which `return` can apply
|
||||
---
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=16228
|
||||
|
||||
int* wrap ( return ref int input )
|
||||
@ -20,5 +20,16 @@ struct S
|
||||
int x;
|
||||
|
||||
int foo() return { return 3; }
|
||||
static ref int foo() return { return x; }
|
||||
static ref int bar() return { return x; }
|
||||
}
|
||||
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=18963
|
||||
|
||||
T Identity(T)(return T t) { return t; }
|
||||
|
||||
void bar(int i, void* p)
|
||||
{
|
||||
Identity(p);
|
||||
Identity(i);
|
||||
}
|
||||
|
13
gcc/testsuite/gdc.test/fail_compilation/test20919.d
Normal file
13
gcc/testsuite/gdc.test/fail_compilation/test20919.d
Normal file
@ -0,0 +1,13 @@
|
||||
/* TEST_OUTPUT:
|
||||
---
|
||||
fail_compilation/test20919.d(12): Error: `__traits(getAttributes, int a)` does not give a valid type
|
||||
---
|
||||
*/
|
||||
// https://issues.dlang.org/show_bug.cgi?id=20919
|
||||
|
||||
void foo(int a) {}
|
||||
|
||||
static if (is(typeof(foo) params == __parameters))
|
||||
{
|
||||
__traits(getAttributes, params) a;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user