Thread overview
Linker anomalies
Nov 07, 2019
Luh
Nov 07, 2019
Ali Çehreli
Nov 07, 2019
Luh
Nov 08, 2019
user5678
November 07, 2019
Hey there,

I figured out some strange behavior ;

#1
It seems that the linker doesn't check for the function declared twice first.
Instead, it says:

"Error: class app.Child use of app.Parent.foo() is hidden by Child; use alias foo = Parent.foo; to introduce base class overload set"

but when we call it ( p.foo() ), then it returns the correct error message.

#2
The template update() compiles fine until we call it.

Bizarre, isn't it ?

code:
---
void main()
{
	auto p = new Parent();
	// Shows the wrong error message until uncommented
	//p.foo();
	// Compiles when commented
	//update(p);
}

class Child : Parent
{
	override void foo() { }
}

class Parent
{
	void foo() { }
	void foo() { }
}

void update(T)(T object) if(is(T == Parent))
{
	static if (is(T == Parent))
	{
		// Shouldn't compile
		ObjectThatDoesntExists.bar(T);
	}
}
---

Is this a bug ?
November 07, 2019
On 11/07/2019 04:14 AM, Luh wrote:
> Hey there,
>
> I figured out some strange behavior ;
>
> #1
> It seems that the linker doesn't check for the function declared twice
> first.

It's not the linker but the "compiler" that is concerned about these things.

> Instead, it says:
>
> "Error: class app.Child use of app.Parent.foo() is hidden by Child; use
> alias foo = Parent.foo; to introduce base class overload set"

That's an important warning where the programmer can get surprising results depending on whether the object is used through the Parent interface or the Child interface.

> but when we call it ( p.foo() ), then it returns the correct error message.

It indicates to me that the compiler is performing certain checks lazily likely for performance reasons.

> #2
> The template update() compiles fine until we call it.

That's common for templates: As long as it's syntactically correct, some errors are delayed until instantiation time.

> Bizarre, isn't it ?

I've seen worse. :)

> code:
> ---
> void main()
> {
>      auto p = new Parent();
>      // Shows the wrong error message until uncommented
>      //p.foo();
>      // Compiles when commented
>      //update(p);
> }
>
> class Child : Parent
> {
>      override void foo() { }
> }
>
> class Parent
> {
>      void foo() { }
>      void foo() { }

Apparently, the compiler does not check whether an overload set has ambiguities until actual use. I suspect the opposite would slow dow compilation and cause frustration in some cases.

> }
>
> void update(T)(T object) if(is(T == Parent))
> {
>      static if (is(T == Parent))
>      {
>          // Shouldn't compile
>          ObjectThatDoesntExists.bar(T);
>      }
> }
> ---
>
> Is this a bug ?

It should compile because ObjectThatDoesntExists may actually be present when T==Parent depending on other conditions in the program. A simple example:

// Called only in some case:
  version (X) update(p);

// And that case happens to introduce ObjectThatDoesntExists:
version (X) {

class O {
  void bar() {
  }
}

O ObjectThatDoesntExists;  // <-- Now the code will compile

} // version

Remember that ObjectThatDoesntExists may even be introduced by another module, which may be compiled separately with e.g. different compilation options (e.g. --version=X). So, it's not possible for the compiler to detect such cases. It's an error only if the template is actually instantiated.

Ali

November 07, 2019
On Thursday, 7 November 2019 at 18:45:21 UTC, Ali Çehreli wrote:
> On 11/07/2019 04:14 AM, Luh wrote:
> It's not the linker but the "compiler" that is concerned about these things.
>
> [...]

Oops :o

> That's an important warning where the programmer can get surprising results depending on whether the object is used through the Parent interface or the Child interface.
> > [...]
> error message.
>
> [...]

It would be clever to show the correct error message :)
(I spend 1 hour to find what was the problem haha. I hope the others newbies won't make the same mistake)

> Apparently, the compiler does not check whether an overload set has ambiguities until actual use. I suspect the opposite would slow dow compilation and cause frustration in some cases.
>
> [...]

Thanks for the tips.
November 08, 2019
On Thursday, 7 November 2019 at 12:14:43 UTC, Luh wrote:
> Hey there,
>
> I figured out some strange behavior ;
>
> #1
> It seems that the linker doesn't check for the function declared twice first.
> Instead, it says:
>
> "Error: class app.Child use of app.Parent.foo() is hidden by Child; use alias foo = Parent.foo; to introduce base class overload set"
>
> but when we call it ( p.foo() ), then it returns the correct error message.
>
> #2
> The template update() compiles fine until we call it.
>
> Bizarre, isn't it ?
>
> code:
> ---
> void main()
> {
> 	auto p = new Parent();
> 	// Shows the wrong error message until uncommented
> 	//p.foo();
> 	// Compiles when commented
> 	//update(p);
> }
>
> class Child : Parent
> {
> 	override void foo() { }
> }
>
> class Parent
> {
> 	void foo() { }
> 	void foo() { }
> }
>
> void update(T)(T object) if(is(T == Parent))
> {
> 	static if (is(T == Parent))
> 	{
> 		// Shouldn't compile
> 		ObjectThatDoesntExists.bar(T);
> 	}
> }
> ---
>
> Is this a bug ?

The diagnostic might be bad but at least the compiler doesn't crash, yay, woohoo !