diff --git a/compiler/src/dmd/dcast.d b/compiler/src/dmd/dcast.d index fdc29094aa0e..6ed351f177d8 100644 --- a/compiler/src/dmd/dcast.d +++ b/compiler/src/dmd/dcast.d @@ -177,6 +177,28 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) return implicitCastTo(result, sc, t); } + // Try opImplicitCast + if (t.ty != Terror && e.type.ty != Terror) + { + import dmd.id : Id; + AggregateDeclaration ad = isAggregate(e.type); + + if (ad) + { + if (Dsymbol fd = search_function(ad, Id.opImplicitCast)) + { + // Rewrite as: e.opImplicitCast!(t)() + auto tiargs = new Objects(); + tiargs.push(t); + Expression ex = new DotTemplateInstanceExp(e.loc, e, fd.ident, tiargs); + ex = new CallExp(e.loc, ex); + ex = ex.expressionSemantic(sc); + if (!ex.isErrorExp()) + return ex; + } + } + } + if (t.ty == Terror || e.type.ty == Terror) return ErrorExp.get(); diff --git a/compiler/src/dmd/id.d b/compiler/src/dmd/id.d index 4ac0f4f3d5e6..fb6aa3b712d7 100644 --- a/compiler/src/dmd/id.d +++ b/compiler/src/dmd/id.d @@ -234,6 +234,7 @@ immutable Msgtable[] msgtable = { "opSliceAssign" }, { "opCall" }, { "opCast" }, + { "opImplicitCast" }, { "opDispatch" }, { "opDollar" }, { "opUnary" }, diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d index 779e6bfd7f59..32b83ff653e6 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -2301,6 +2301,36 @@ extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, Argu buf.reset(); buf.writestring(failMessage); } + // Try opImplicitCast if direct match failed + if (m == MATCH.nomatch && sc && !sc.traitsCompiles && sc.intypeof != 1) + { + AggregateDeclaration ad = isAggregate(arg.type); + if (ad) + { + if (Dsymbol castFn = search_function(ad, Id.opImplicitCast)) + { + // Rewrite as: arg.opImplicitCast!(p.type)() + auto tiargs = new Objects(); + tiargs.push(p.type); + Expression ex = new DotTemplateInstanceExp(arg.loc, arg, castFn.ident, tiargs); + ex = new CallExp(arg.loc, ex); + + // Sema to avoid error message spam + uint errors = global.startGagging(); + ex = ex.expressionSemantic(sc); + if (!global.endGagging(errors) && !ex.isErrorExp()) + { + // Retry match with the converted expression + m = argumentMatchParameter(fd, tf, p, ex, wildmatch, flag, sc, pMessage); + if (m != MATCH.nomatch) + { + // Update the argument for the actual call + args[u] = ex; + } + } + } + } + } } else if (p.defaultArg) continue; diff --git a/compiler/test/fail_compilation/opimplicitcast.d b/compiler/test/fail_compilation/opimplicitcast.d new file mode 100644 index 000000000000..7bb04fb05a9d --- /dev/null +++ b/compiler/test/fail_compilation/opimplicitcast.d @@ -0,0 +1,114 @@ +// REQUIRED_ARGS: -o- + +/* +TEST_OUTPUT: +--- +fail_compilation/opimplicitcast.d(18): Error: cannot implicitly convert expression `s` of type `NoCastStruct` to `int` +--- +*/ +// No matching opImplicitCast +struct NoCastStruct +{ + int value; +} + +void testNoCast() +{ + NoCastStruct s; + int x = s; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/opimplicitcast.d(45): Error: template instance `opimplicitcast.WrongReturnStruct.opImplicitCast!int` does not match template declaration `opImplicitCast(T)()` + with `T = int` + must satisfy the following constraint: +` __traits(isSame, T, string)` +fail_compilation/opimplicitcast.d(45): Error: cannot implicitly convert expression `s` of type `WrongReturnStruct` to `int` +--- +*/ +// opImplicitCast with wrong constraint +struct WrongReturnStruct +{ + int value; + + T opImplicitCast(T)() if (__traits(isSame, T, string)) + { + return "hello"; + } +} + +void testWrongReturn() +{ + WrongReturnStruct s; + int x = s; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/opimplicitcast.d(63): Error: cannot implicitly convert expression `c` of type `opimplicitcast.NoCastClass` to `int` +--- +*/ +// Class without opImplicitCast +class NoCastClass +{ + int value; +} + +void testClassNoCast() +{ + NoCastClass c = new NoCastClass(); + int x = c; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/opimplicitcast.d(79): Error: undefined identifier `NonExistent` +fail_compilation/opimplicitcast.d(86): Error: template instance `opimplicitcast.ErrorCastStruct.opImplicitCast!int` error instantiating +fail_compilation/opimplicitcast.d(86): Error: cannot implicitly convert expression `s` of type `ErrorCastStruct` to `int` +--- +*/ +// opImplicitCast with semantic error +struct ErrorCastStruct +{ + T opImplicitCast(T)() if (__traits(isSame, T, int)) + { + return NonExistent.value; + } +} + +void testErrorCast() +{ + ErrorCastStruct s; + int x = s; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/opimplicitcast.d(113): Error: template instance `opimplicitcast.NoMatchStruct.opImplicitCast!string` does not match template declaration `opImplicitCast(T)()` + with `T = string` + must satisfy the following constraint: +` __traits(isSame, T, int)` +fail_compilation/opimplicitcast.d(113): Error: cannot implicitly convert expression `s` of type `NoMatchStruct` to `string` +--- +*/ +// opImplicitCast exists but not for requested type +struct NoMatchStruct +{ + int value; + + T opImplicitCast(T)() if (__traits(isSame, T, int)) + { + return value; + } +} + +void testNoMatch() +{ + NoMatchStruct s; + string x = s; +} diff --git a/compiler/test/runnable/opimplicitcast.d b/compiler/test/runnable/opimplicitcast.d new file mode 100644 index 000000000000..e6f4932fc33a --- /dev/null +++ b/compiler/test/runnable/opimplicitcast.d @@ -0,0 +1,251 @@ +// Basic opImplicitCast to enum (enum type inference) +enum MyEnum { A, B, C } + +struct Symbol(string name) +{ + T opImplicitCast(T)() + { + return __traits(getMember, T, name); + } + + bool opEquals(T)(T t) const + { + return t == __traits(getMember, T, name); + } +} + +struct _ +{ + static ref enum opDispatch(string name) = Symbol!name.init; +} + +void test1() +{ + MyEnum something = _.B; + assert(something == MyEnum.B); + + something = _.A; + assert(something == MyEnum.A); +} + +// opImplicitCast with switch case +void test2() +{ + MyEnum e = MyEnum.B; + int result; + + switch (e) + { + case _.A: result = 1; break; + case _.B: result = 2; break; + case _.C: result = 3; break; + default: result = 0; + } + + assert(result == 2); +} + +// opImplicitCast to basic types +struct IntWrapper +{ + int value; + + this(int v) { value = v; } + + T opImplicitCast(T)() const if (__traits(isSame, T, int)) + { + return value; + } +} + +void test3() +{ + IntWrapper iw = IntWrapper(42); + int x = iw; + assert(x == 42); +} + +// opImplicitCast in function arguments +void takeInt(int x) +{ + assert(x == 100); +} + +void takeEnum(MyEnum e) +{ + assert(e == MyEnum.C); +} + +void test4() +{ + IntWrapper iw = IntWrapper(100); + takeInt(iw); + + takeEnum(_.C); +} + +// opImplicitCast with multiple target types +struct MultiCast +{ + int value; + + this(int v) { value = v; } + + T opImplicitCast(T)() const + { + static if (__traits(isSame, T, int)) + return value; + else static if (__traits(isSame, T, long)) + return cast(long)value; + else static if (__traits(isSame, T, double)) + return cast(double)value; + else + static assert(false, "Unsupported type"); + } +} + +void test5() +{ + MultiCast mc = MultiCast(50); + int i = mc; + long l = mc; + double d = mc; + + assert(i == 50); + assert(l == 50L); + assert(d == 50.0); +} + +// opImplicitCast in struct with const +struct ConstCast +{ + int value; + + this(int v) { value = v; } + + T opImplicitCast(T)() const if (__traits(isSame, T, int)) + { + return value; + } +} + +void test6() +{ + const ConstCast cc = ConstCast(200); + int x = cc; + assert(x == 200); +} + +// Nested struct with opImplicitCast +struct InnerWrapper +{ + int value; + + T opImplicitCast(T)() if (__traits(isSame, T, int)) + { + return value; + } +} + +struct OuterWrapper +{ + InnerWrapper inner; + + T opImplicitCast(T)() if (__traits(isSame, T, int)) + { + return inner.opImplicitCast!T(); + } +} + +void test7() +{ + OuterWrapper ow; + ow.inner.value = 300; + int x = ow; + assert(x == 300); +} + +// opImplicitCast with class types +class MyClass +{ + int value; + this(int v) { value = v; } +} + +struct ClassWrapper +{ + MyClass obj; + + this(int v) { obj = new MyClass(v); } + + T opImplicitCast(T)() if (is(T == MyClass)) + { + return obj; + } +} + +void test8() +{ + ClassWrapper cw = ClassWrapper(500); + MyClass c = cw; + assert(c.value == 500); +} + +// opImplicitCast with template constraints +struct ConstrainedCast +{ + long value; + + this(long v) { value = v; } + + T opImplicitCast(T)() if (is(T : long)) + { + return cast(T)value; + } +} + +void test9() +{ + ConstrainedCast cc = ConstrainedCast(1000); + int i = cc; + long l = cc; + + assert(i == 1000); + assert(l == 1000); +} + +// opImplicitCast in array indexing context +struct IndexWrapper +{ + size_t idx; + + this(size_t i) { idx = i; } + + T opImplicitCast(T)() if (__traits(isSame, T, size_t)) + { + return idx; + } +} + +void test10() +{ + int[5] arr = [10, 20, 30, 40, 50]; + IndexWrapper iw = IndexWrapper(2); + size_t idx = iw; + assert(arr[idx] == 30); +} + + +void main() +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + test10(); +}