Bug 14675 – template specialization for T: T* and T: T[] has issues with IFTI

Status
NEW
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2015-06-10T11:57:20Z
Last change time
2024-12-13T18:43:18Z
Keywords
spec
Assigned to
No Owner
Creator
Steven Schveighoffer
Moved to GitHub: dmd#19001 →

Comments

Comment #0 by schveiguy — 2015-06-10T11:57:20Z
Some background: http://forum.dlang.org/post/[email protected] When a specialization of the form: void foo(T : T*)(T* t) void foo(T : T[])(T[] t) exists in a function template, it cannot be matched for IFTI, only explicit instantiation: int i; foo(&i); // error foo!(int *)(&i); // ok However, this will match both calls: void foo(T : U*, U)(T t) void foo(T : U[], U)(T t) The docs say that parameters involved with specialization cannot be used for IFTI, but that simply isn't the case for almost all specializations (2 examples are the secondary forms above). Just these 2 as far as I can tell don't work. First, their existence is redundant. The secondary forms are superior in almost every way. Second, they are confusing. A T is specialized as a T*? But then it's not? Third, if they must exist, I think they should be usable with IFTI. There doesn't seem to be any reasonable explanation as to why these particular templates cannot be used for IFTI. As an example of why this is important, consider the following: void foo(T)(T t) { /* impl 1 */} void foo(T:T*)(T* t) { /* impl 2 */} int i; foo(&i); // calls impl 1 foo!(typeof(&i))(i) // calls impl 2 This makes absolutely no sense, and seems extremely error prone. Valid resolutions here: 1. Deprecate. This would break some code, but the ability to use the second form at least gives a path forward. 2. Allow IFTI. At least this would not break code (at least not *reasonable* code), but could potentially change code paths. It's possible someone has exploited this confusing behavior to have two code paths for calling a function with the same parameters. I don't think we should support this though.
Comment #1 by k.hara.pg — 2015-06-11T01:54:56Z
Personally I call the things `T : T*` "self-dependent" or "self-specialized" template parameter. I agree that is one of a strange feature in D. I guess it would be a syntax to declare a type parameter with specialization at once. // C++ template <typename T> class C; template <typename T> class C<T*> { ... } // Equivalent D code class C(T : T*) { ... } It takes one place in template parameter list. When a template argument is _explicitly given_ on the position, it will deduce the parameter T, by matching the argument to the form `T*`. That's the point. Even if you explicitly give template argument, T is always deduced. On the other hand, IFTI deduces T from the function argument types before testing the specialization. After the T determined, specialization test always fail because T won't match to T*. Therefore, we cannot make possible using it along with IFTI.
Comment #2 by schveiguy — 2015-06-11T15:03:36Z
Well, if T: T* isn't going to be removed, and we cannot use IFTI with it, we should at least update the documentation. The documentation doesn't seem accurate, since specializations do not disqualify IFTI obviously. There is definitely room to add docs to template specialization. I'll see if I can update the docs to guide people away from making these mistakes. (In reply to Kenji Hara from comment #1) > It takes one place in template parameter list. When a template argument is > _explicitly given_ on the position, it will deduce the parameter T, by > matching the argument to the form `T*`. > That's the point. Even if you explicitly give template argument, T is always > deduced. This doesn't make a whole lot of sense, because in most cases, when you EXPLICITLY specify a template parameter, you are specifying the parameter on the left of the colon, and it only matches the specialization if it matches the pattern on the right: foo(T : ulong)(T t) foo(T : U*, U)(T t) foo!int(1)); // T == int foo!(int *)(null); // T == int * But in this case, the instantiation doesn't specify T, it specifies, well, I don't know *what* it specifies! foo(T : T*)(T *) foo!(int *)(null); // T == int So you CAN'T specify T directly, and you can't access the parameter you DID specify directly. I feel like it's shorthand for this: foo(U : T*, T)(U *t) But U isn't actually listed or accessible. BTW, the above has the same issue with IFTI. I think this is an anti-pattern that should always be avoided. And I think that the T* and T[] forms are the only specializations that fall under this pattern. Is there any other time you can explicitly instantiate a template, but the parameter you explicitly pass is not used for the actual parameter?
Comment #3 by schveiguy — 2015-06-11T15:05:29Z
(In reply to Steven Schveighoffer from comment #2) > I feel like it's shorthand for this: > > foo(U : T*, T)(U *t) > > But U isn't actually listed or accessible. BTW, the above has the same issue > with IFTI. gah, wish I could edit. That should read: foo(U : T*, T)(T *t)
Comment #4 by robert.schadek — 2024-12-13T18:43:18Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19001 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB