Improve default argument processing #1102

Merged
merged 6 commits into from Jul 4, 2013
View
2 src/cppmangle.c
@@ -428,7 +428,7 @@ static int argsCppMangleDg(void *ctx, size_t n, Parameter *arg)
{
ArgsCppMangleCtx *p = (ArgsCppMangleCtx *)ctx;
- Type *t = arg->type;
+ Type *t = arg->type->merge2();
if (arg->storageClass & (STCout | STCref))
t = t->referenceTo();
else if (arg->storageClass & STClazy)
View
6 src/ctfeexpr.c
@@ -366,7 +366,7 @@ Expression *copyLiteral(Expression *e)
*/
Expression *paintTypeOntoLiteral(Type *type, Expression *lit)
{
- if (lit->type == type)
+ if (lit->type->equals(type))
return lit;
Expression *e;
if (lit->op == TOKslice)
@@ -1758,10 +1758,10 @@ void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef)
assert( ae->type->ty == Tsarray || ae->type->ty == Tarray);
#if DMDV2
Type *desttype = ((TypeArray *)ae->type)->next->castMod(0);
- bool directblk = (val->type->toBasetype()->castMod(0)) == desttype;
+ bool directblk = (val->type->toBasetype()->castMod(0))->equals(desttype);
#else
Type *desttype = ((TypeArray *)ae->type)->next;
- bool directblk = (val->type->toBasetype()) == desttype;
+ bool directblk = (val->type->toBasetype())->equals(desttype);
#endif
bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral
View
7 src/declaration.c
@@ -562,6 +562,13 @@ void AliasDeclaration::semantic(Scope *sc)
else if (t)
{
type = t->semantic(loc, sc);
+
+ /* TypeTuple might have extra info,
+ * is(typeof(func) PT == __parameters)
+ * Otherwise, strip all redundant informations.
+ */
+ if (type->ty != Ttuple)
+ type = type->merge2();
//printf("\talias resolved to type %s\n", type->toChars());
}
if (overnext)
View
5 src/expression.c
@@ -1322,7 +1322,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
if (!arg)
{
- if (!p->defaultArg || !fd)
+ if (!p->defaultArg)
{
if (tf->varargs == 2 && i + 1 == nparams)
goto L2;
@@ -1483,7 +1483,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
arg = arg->implicitCastTo(sc, p->type->substWildTo(wildmatch));
arg = arg->optimize(WANTvalue, p->storageClass & STCref);
}
- else if (p->type != arg->type)
+ else if (!p->type->equals(arg->type))
{
//printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars());
if (arg->op == TOKtype)
@@ -13056,6 +13056,7 @@ Expression *CondExp::semantic(Scope *sc)
e2 = e2->castTo(sc, type);
}
}
+ type = type->merge2();
#if 0
printf("res: %s\n", type->toChars());
printf("e1 : %s\n", e1->type->toChars());
View
6 src/func.c
@@ -1420,7 +1420,7 @@ void FuncDeclaration::semantic3(Scope *sc)
::error(loc, "%s '%s' is nothrow yet may throw", kind(), toPrettyChars());
if (flags & FUNCFLAGnothrowInprocess)
{
- if (type == f) f = f->copy();
+ if (type == f) f = (TypeFunction *)f->copy();
f->isnothrow = !(blockexit & BEthrow);
}
@@ -1755,14 +1755,14 @@ void FuncDeclaration::semantic3(Scope *sc)
if (flags & FUNCFLAGpurityInprocess)
{
flags &= ~FUNCFLAGpurityInprocess;
- if (type == f) f = f->copy();
+ if (type == f) f = (TypeFunction *)f->copy();
f->purity = PUREfwdref;
}
if (flags & FUNCFLAGsafetyInprocess)
{
flags &= ~FUNCFLAGsafetyInprocess;
- if (type == f) f = f->copy();
+ if (type == f) f = (TypeFunction *)f->copy();
f->trust = TRUSTsafe;
}
View
14 src/interpret.c
@@ -3042,9 +3042,9 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
{
desttype = ((TypeArray *)desttype)->next;
#if DMDV2
- if (srctype == desttype->castMod(0))
+ if (srctype->equals(desttype->castMod(0)))
#else
- if (srctype == desttype)
+ if (srctype->equals(desttype))
#endif
{
isBlockAssignment = true;
@@ -3064,7 +3064,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
bool wantRef = false;
bool wantLvalueRef = false;
- if (!fp && this->e1->type->toBasetype() == this->e2->type->toBasetype() &&
+ if (!fp && this->e1->type->toBasetype()->equals(this->e2->type->toBasetype()) &&
(e1->type->toBasetype()->ty == Tarray || isAssocArray(e1->type)
|| e1->type->toBasetype()->ty == Tclass)
// e = *x is never a reference, because *x is always a value
@@ -4089,10 +4089,10 @@ Expression *interpretAssignToSlice(InterState *istate, CtfeGoal goal, Loc loc,
existingAE->type->ty == Tarray);
#if DMDV2
Type *desttype = ((TypeArray *)existingAE->type)->next->castMod(0);
- bool directblk = (e2->type->toBasetype()->castMod(0)) == desttype;
+ bool directblk = (e2->type->toBasetype()->castMod(0))->equals(desttype);
#else
Type *desttype = ((TypeArray *)existingAE->type)->next;
- bool directblk = (e2->type->toBasetype()) == desttype;
+ bool directblk = (e2->type->toBasetype())->equals(desttype);
#endif
bool cow = !(newval->op == TOKstructliteral || newval->op == TOKarrayliteral
|| newval->op == TOKstring);
@@ -5268,7 +5268,7 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal)
e->type = type;
return e;
}
- if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type != e1->type)
+ if (e1->op == TOKindex && !((IndexExp *)e1)->e1->type->equals(e1->type))
{ // type painting operation
IndexExp *ie = (IndexExp *)e1;
e = new IndexExp(e1->loc, ie->e1, ie->e2);
@@ -5661,7 +5661,7 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal)
* CastExp.
*/
if (goal == ctfeNeedLvalue && e->op == TOKindex &&
- e->type == type &&
+ e->type->equals(type) &&
isPointer(type) )
return e;
// ...Otherwise, just return the (simplified) dotvar expression
View
119 src/mtype.c
@@ -154,6 +154,13 @@ const char *Type::kind()
return NULL;
}
+Type *Type::copy()
+{
+ Type *t = (Type *)mem.malloc(sizeTy[ty]);
+ memcpy(t, this, sizeTy[ty]);
+ return t;
+}
+
Type *Type::syntaxCopy()
{
print();
@@ -1176,10 +1183,15 @@ Type *Type::pointerTo()
if (ty == Terror)
return this;
if (!pto)
- { Type *t;
-
- t = new TypePointer(this);
- pto = t->merge();
+ {
+ Type *t = new TypePointer(this);
+ if (ty == Tfunction)
+ {
+ t->deco = t->merge()->deco;
+ pto = t;
+ }
+ else
+ pto = t->merge();
}
return pto;
}
@@ -1597,6 +1609,85 @@ char *Type::modToChars()
}
/************************************
+ * Strip all parameter's idenfiers and their default arguments for merging types.
+ * If some of parameter types or return type are function pointer, delegate, or
+ * the types which contains either, then strip also from them.
+ */
+
+Type *stripDefaultArgs(Type *t)
+{
+ struct N
+ {
+ static Parameters *stripParams(Parameters *arguments)
+ {
+ Parameters *args = arguments;
+ if (args && args->dim > 0)
+ {
+ for (size_t i = 0; i < args->dim; i++)
+ {
+ Parameter *a = (*args)[i];
+ Type *ta = stripDefaultArgs(a->type);
+ if (ta != a->type || a->defaultArg || a->ident)
+ {
+ if (args == arguments)
+ {
+ args = new Parameters();
+ args->setDim(arguments->dim);
+ for (size_t j = 0; j < args->dim; j++)
+ (*args)[j] = (*arguments)[j];
+ }
+ (*args)[i] = new Parameter(a->storageClass, ta, NULL, NULL);
+ }
+ }
+ }
+ return args;
+ }
+ };
+
+ if (t == NULL)
+ return t;
+
+ if (t->ty == Tfunction)
+ {
+ TypeFunction *tf = (TypeFunction *)t;
+ Type *tret = stripDefaultArgs(tf->next);
+ Parameters *args = N::stripParams(tf->parameters);
+ if (tret == tf->next && args == tf->parameters)
+ goto Lnot;
+ tf = (TypeFunction *)tf->copy();
+ tf->parameters = args;
+ tf->next = tret;
+ //printf("strip %s\n <- %s\n", tf->toChars(), t->toChars());
+ t = tf;
+ }
+ else if (t->ty == Ttuple)
+ {
+ TypeTuple *tt = (TypeTuple *)t;
+ Parameters *args = N::stripParams(tt->arguments);
+ if (args == tt->arguments)
+ goto Lnot;
+ t = new TypeTuple(args);
+ }
+ else if (t->ty == Tenum)
+ {
+ // TypeEnum::nextOf() may be != NULL, but it's not necessary here.
+ goto Lnot;
+ }
+ else
+ {
+ Type *tn = t->nextOf();
+ Type *n = stripDefaultArgs(tn);
+ if (n == tn)
+ goto Lnot;
+ t = t->copy();
+ ((TypeNext *)t)->next = n;
+ }
+ //printf("strip %s\n", t->toChars());
+Lnot:
+ return t;
+}
+
+/************************************
*/
Type *Type::merge()
@@ -1633,8 +1724,8 @@ Type *Type::merge()
}
else
{
- sv->ptrvalue = this;
- deco = (char *)sv->toDchars();
+ sv->ptrvalue = (t = stripDefaultArgs(t));
+ deco = t->deco = (char *)sv->toDchars();
//printf("new value, deco = '%s' %p\n", t->deco, t->deco);
}
}
@@ -4394,6 +4485,7 @@ Type *TypeAArray::semantic(Loc loc, Scope *sc)
}
else
index = index->semantic(loc,sc);
+ index = index->merge2();
if (index->nextOf() && !index->nextOf()->isImmutable())
{
@@ -4420,7 +4512,7 @@ printf("index->ito->ito = x%x\n", index->ito->ito);
case Terror:
return Type::terror;
}
- next = next->semantic(loc,sc);
+ next = next->semantic(loc,sc)->merge2();
transitive();
switch (next->toBasetype()->ty)
@@ -4723,7 +4815,7 @@ Type *TypePointer::semantic(Loc loc, Scope *sc)
{ transitive();
return merge();
}
-#if 1
+#if 0
return merge();
#else
deco = merge()->deco;
@@ -4968,13 +5060,6 @@ const char *TypeFunction::kind()
return "function";
}
-TypeFunction *TypeFunction::copy()
-{
- TypeFunction *tf = (TypeFunction *)mem.malloc(sizeof(TypeFunction));
- memcpy(tf, this, sizeof(TypeFunction));
- return tf;
-}
-
Type *TypeFunction::syntaxCopy()
{
Type *treturn = next ? next->syntaxCopy() : NULL;
@@ -5406,7 +5491,7 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc)
* This can produce redundant copies if inferring return type,
* as semantic() will get called again on this.
*/
- TypeFunction *tf = copy();
+ TypeFunction *tf = (TypeFunction *)copy();
if (parameters)
{ tf->parameters = (Parameters *)parameters->copy();
for (size_t i = 0; i < parameters->dim; i++)
@@ -6123,7 +6208,7 @@ Type *TypeDelegate::semantic(Loc loc, Scope *sc)
* be removed from next before the merge.
*/
-#if 1
+#if 0
return merge();
#else
/* Don't return merge(), because arg identifiers and default args
View
2 src/mtype.h
@@ -228,6 +228,7 @@ class Type : public RootObject
Type(TY ty);
virtual const char *kind();
+ Type *copy();
virtual Type *syntaxCopy();
bool equals(RootObject *o);
int dyncast() { return DYNCAST_TYPE; } // kludge for template.isType()
@@ -669,7 +670,6 @@ class TypeFunction : public TypeNext
TypeFunction(Parameters *parameters, Type *treturn, int varargs, LINK linkage, StorageClass stc = 0);
const char *kind();
- TypeFunction *copy();
Type *syntaxCopy();
Type *semantic(Loc loc, Scope *sc);
void purityLevel();
View
10 test/fail_compilation/fail3866.d
@@ -1,10 +0,0 @@
-
-void main() {
-
- auto foo = (int a = 1) { return a;};
- auto bar = (int a) { return a;};
-
- foo();
- bar();
-}
-
View
263 test/runnable/functype.d
@@ -0,0 +1,263 @@
+extern(C) int printf(const char*, ...);
+
+/***************************************************/
+
+void testfp()
+{
+ static int func1(int n = 1) { return n; }
+ static int func2(int n ) { return n; }
+ static assert(typeof(func1).stringof == "int(int n = 1)");
+ static assert(typeof(func2).stringof == "int(int n)");
+ static assert( is(typeof(func1()))); // OK
+ static assert(!is(typeof(func2()))); // NG
+
+ alias typeof(func1) Func1;
+ alias typeof(func2) Func2;
+ static assert(is(Func1 == Func2));
+ static assert(Func1.stringof == "int(int)"); // removed defaultArgs
+ static assert(Func2.stringof == "int(int)");
+
+ auto fp1 = &func1;
+ auto fp2 = &func2;
+ static assert(typeof(fp1).stringof == "int function(int n = 1)");
+ static assert(typeof(fp2).stringof == "int function(int n)");
+ static assert( is(typeof(fp1()))); // OK
+ static assert(!is(typeof(fp2()))); // NG
+
+ alias typeof(fp1) Fp1;
+ alias typeof(fp2) Fp2;
+ static assert(is(Fp1 == Fp2));
+ static assert(Fp1.stringof == "int function(int)"); // removed defaultArgs
+ static assert(Fp2.stringof == "int function(int)");
+
+ typeof(fp1) fp3 = fp1;
+ typeof(fp2) fp4 = fp2;
+ static assert(is(typeof(fp3) == typeof(fp4)));
+ static assert(typeof(fp3).stringof == "int function(int n = 1)");
+ static assert(typeof(fp4).stringof == "int function(int n)");
+ static assert( is(typeof(fp3()))); // OK
+ static assert(!is(typeof(fp4()))); // NG
+
+ alias typeof(fp3) Fp3;
+ alias typeof(fp4) Fp4;
+ static assert(is(Fp3 == Fp4));
+ static assert(Fp3.stringof == "int function(int)"); // removed defaultArgs
+ static assert(Fp4.stringof == "int function(int)");
+
+ auto fplit1 = function(int n = 1) { return n; };
+ auto fplit2 = function(int n ) { return n; };
+ static assert( is(typeof(fplit1()))); // OK
+ static assert(!is(typeof(fplit2()))); // NG
+}
+
+void testdg()
+{
+ int nest1(int n = 1) { return n; }
+ int nest2(int n ) { return n; }
+ static assert(typeof(nest1).stringof == "int(int n = 1)");
+ static assert(typeof(nest2).stringof == "int(int n)");
+ static assert( is(typeof(nest1()))); // OK
+ static assert(!is(typeof(nest2()))); // NG
+
+ alias typeof(nest1) Nest1;
+ alias typeof(nest2) Nest2;
+ static assert(is(Nest1 == Nest2));
+ static assert(Nest1.stringof == "int(int)"); // removed defaultArgs
+ static assert(Nest2.stringof == "int(int)");
+
+ auto dg1 = &nest1;
+ auto dg2 = &nest2;
+ static assert(typeof(dg1).stringof == "int delegate(int n = 1)");
+ static assert(typeof(dg2).stringof == "int delegate(int n)");
+ static assert( is(typeof(dg1()))); // OK
+ static assert(!is(typeof(dg2()))); // NG
+
+ alias typeof(dg1) Dg1;
+ alias typeof(dg2) Dg2;
+ static assert(is(Dg1 == Dg2));
+ static assert(Dg1.stringof == "int delegate(int)"); // removed defaultArgs
+ static assert(Dg2.stringof == "int delegate(int)");
+
+ typeof(dg1) dg3 = dg1;
+ typeof(dg2) dg4 = dg2;
+ static assert(typeof(dg3).stringof == "int delegate(int n = 1)");
+ static assert(typeof(dg4).stringof == "int delegate(int n)");
+ static assert( is(typeof(dg3()))); // OK
+ static assert(!is(typeof(dg4()))); // NG
+
+ alias typeof(dg3) Dg3;
+ alias typeof(dg4) Dg4;
+ static assert(is(Dg3 == Dg4));
+ static assert(Dg3.stringof == "int delegate(int)"); // removed defaultArgs
+ static assert(Dg4.stringof == "int delegate(int)");
+
+ auto dglit1 = delegate(int n = 1) { return n; };
+ auto dglit2 = delegate(int n ) { return n; };
+ static assert( is(typeof(dglit1()))); // OK
+ static assert(!is(typeof(dglit2()))); // NG
+}
+
+void testda()
+{
+ // Unsupported cases with current implementation.
+
+ int function(int n = 1)[] fpda = [n => n + 1, n => n+2];
+ assert(fpda[0](1) == 2);
+ assert(fpda[1](1) == 3);
+ static assert(!is(typeof(fpda[0]() == 1))); // cannot call with using defArgs
+ static assert(!is(typeof(fpda[1]() == 2))); // cannot call with using defArgs
+ static assert(typeof(fpda).stringof == "int function(int)[]");
+ static assert(typeof(fpda).stringof != "int funciton(int n = 1)[]");
+
+ int delegate(int n = 1)[] dgda = [n => n + 1, n => n+2];
+ assert(dgda[0](1) == 2);
+ assert(dgda[1](1) == 3);
+ static assert(!is(typeof(dgda[0]() == 1))); // cannot call with using defArgs
+ static assert(!is(typeof(dgda[1]() == 2))); // cannot call with using defArgs
+ static assert(typeof(dgda).stringof == "int delegate(int)[]");
+ static assert(typeof(fpda).stringof != "int delegate(int n = 1)[]");
+}
+
+template StringOf(T)
+{
+ // template type parameter cannot have redundant informations
+ enum StringOf = T.stringof;
+}
+
+void testti()
+{
+ int[] test(int[] a = []) { return a; }
+ static assert(typeof(test).stringof == "int[](int[] a = [])");
+ static assert(StringOf!(typeof(test)) == "int[](int[])");
+
+ float function(float x = 0F) fp = x => x;
+ static assert(typeof(fp).stringof == "float function(float x = " ~ (0F).stringof ~ "F)");
+ static assert(StringOf!(typeof(fp)) == "float function(float)");
+
+ double delegate(double x = 0.0) dg = x => x;
+ static assert(typeof(dg).stringof == "double delegate(double x = " ~ (0.0).stringof ~ ")");
+ static assert(StringOf!(typeof(dg)) == "double delegate(double)");
+}
+
+void testxx()
+{
+ // The corner cases which I had introduced in forum discussion
+
+ // f will inherit default args from its initializer, if it's declared with 'auto'
+ auto f1 = (int n = 10){ return 10; };
+ assert(f1() == 10);
+
+ // what is the actual default arg of f?
+ int function(int n = 10) f2 = (int n = 20){ return n; };
+ int function(int n ) f3 = (int n = 30){ return n; };
+ int function(int n = 40) f4 = (int n ){ return n; };
+ assert(f2() == 10);
+ static assert(!is(typeof(f3())));
+ assert(f4() == 40);
+
+ // conditional expression and the type of its result
+ auto f5 = true ? (int n = 10){ return n; }
+ : (int n = 20){ return n; } ;
+ auto f6 = true ? (int n = 10, string s = "hello"){ return n; }
+ : (int n = 10, string s = "world"){ return n; } ;
+ static assert(!is(typeof(f5()))); // cannot call
+ static assert(!is(typeof(f6()))); // cannot call
+
+ int function(int n = 10) f7; // default arg of the first parameter is 10
+ f7 = (int n = 20){ return n; }; // function literal's default arg will be ignored
+ assert(f7() == 10);
+
+ // returning function pointer/delegate type can have default args
+ int delegate(int n = 10) foo(int x) { return n => n + x; }
+ auto f = foo(1);
+ assert(f() == 11);
+}
+
+/***************************************************/
+// 3646
+
+int bar3646(int x = 10) { printf("bar %d\n", x); return x; }
+int bam3646(int y) { printf("bam %d\n", y); return y; }
+
+int qux3646() { printf("quux\n"); return 20; }
+int qux3646(int i) { printf("quux %d\n", i); return 30; }
+
+void foo3646a(Fn)(Fn fn)
+{
+ fn();
+}
+
+void foo3646b(alias fn)(int res1, int res2)
+{
+ assert(fn() == res1);
+ assert(fn(42) == res2);
+}
+
+void test3646()
+{
+ static assert(!is(typeof(foo3646(&bar3646))));
+ static assert(!is(typeof(foo3646(&bam3646))));
+ static assert(typeof(&bar3646).stringof == "int function(int x = 10)");
+ static assert(typeof(&bam3646).stringof == "int function(int y)");
+
+ foo3646b!bar3646(10, 42);
+ static assert(!is(typeof(foo3646b!bam3646(0, 0))));
+ foo3646b!qux3646(20, 30);
+}
+
+/***************************************************/
+// 3866
+
+void test3866()
+{
+
+ auto foo = (int a = 1) { return a; };
+ auto bar = (int a) { return a; };
+
+ assert(foo() == 1);
+ static assert(!is(typeof(bar())));
+ assert(foo(2) == 2);
+ assert(bar(3) == 3);
+}
+
+/***************************************************/
+// 8579
+
+void test8579()
+{
+ static void func1(int i, double j = 1.0) {}
+ static void func2(int x, double y) {}
+ auto fn1 = &func1;
+ auto fn2 = &func2;
+ static assert(is(typeof(fn1) == typeof(fn2)));
+ assert( typeid(fn1) is typeid(fn2) );
+ static assert(typeof(fn1).stringof == "void function(int i, double j = " ~ (1.0).stringof ~ ")");
+ static assert(typeof(fn2).stringof == "void function(int x, double y)");
+
+ static int func3(int x, double y) { return 0; }
+ static int func4(int i, double j = 1.0) { return 0; }
+ auto fn3 = &func3;
+ auto fn4 = &func4;
+ static assert(is(typeof(fn3) == typeof(fn4)));
+ assert( typeid(fn3) is typeid(fn4) );
+ static assert(typeof(fn3).stringof == "int function(int x, double y)");
+ static assert(typeof(fn4).stringof == "int function(int i, double j = " ~ (1.0).stringof ~ ")");
+}
+
+/***************************************************/
+
+int main()
+{
+ testfp();
+ testdg();
+ testda();
+ testti();
+ testxx();
+ test3646();
+ test3866();
+ test8579();
+
+ printf("Success\n");
+ return 0;
+}
+