Skip to content

Collision of interface constant and trait constant should not produce fatal error #16198

Closed

Description

@Renji-FR

Description

The following code:

<?php
trait T1
{
	final public const string C1 = 'T1';
}

interface I1
{
	public const ?string C1 = null;
	public const ?string C2 = null;
}

final class O1 implements I1
{
	final public const string C2 = 'O1';
}

final class O2 implements I1
{
	use T1;
}

abstract class A1 implements I1
{
}

final class O3 extends A1
{
	final public const string C2 = 'O3';
}

final class O4 extends A1
{
	use T1;
}

Resulted in this output:

Fatal error: I1 and T1 define the same constant (C1) in the composition of O4. However, the definition differs and is considered incompatible. Class was composed in /home/user/scripts/code.php on line 32

But I expected this output instead:

No runtime error

This bug is related to the ticket #9271.


The behavior of a trait is to copy code into class, so if we can do something without trait and without error, it should be possible to do the same thing but with trait. I don't understand why I get this error with my code.

PHP Version

PHP 8.3.9

Operating System

Ubuntu 22.04

Activity

  1. cmb69 commented on Oct 3, 2024

    @cmb69
    Member

    Well, could go beyond this error with the following patch

     Zend/zend_inheritance.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
    index 88f1d3447c..fed00589f4 100644
    --- a/Zend/zend_inheritance.c
    +++ b/Zend/zend_inheritance.c
    @@ -2689,7 +2689,7 @@ static void emit_incompatible_trait_constant_error(
     static bool do_trait_constant_check(
     	zend_class_entry *ce, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait
     ) {
    -	uint32_t flags_mask = ZEND_ACC_PPP_MASK | ZEND_ACC_FINAL;
    +	uint32_t flags_mask = ZEND_ACC_PPP_MASK;
     
     	zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
     	if (zv == NULL) {

    But then the compilation would fail, because trait constants are apparently invariant:

    inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type);
    inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type);
    if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) {

    When lifting the restriction, the compilation would then fail because the constant values are not compatible (i.e. not the same, as is checked in this case):

    if (!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) {
    /* There is an existing constant of the same name, and it conflicts with the new one, so let's throw a fatal error */
    emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
    return false;
    }

  2. Renji-FR commented on Oct 4, 2024

    @Renji-FR
    Author

    @cmb69 Thank you, so the fix requires more dev or it can not be fixed?
    The behavior is different with trait and could not be fixed?

  3. cmb69 commented on Oct 4, 2024

    @cmb69
    Member

    Hmm, I think it can be "fixed", but I'm not sure whether the current implementation is by design. Maybe @kocsismate can explain the rationale of the current implementation.

  4. iluuu1994 commented on Oct 8, 2024

    @iluuu1994
    Member

    Likely related to #15753, with a fix here. I quickly applied it to 8.3 and tried the test, and it indeed passes.

  5. Renji-FR commented on Oct 8, 2024

    @Renji-FR
    Author

    related to #16198

    Thank you @iluuu1994

  6. linked a pull request that will close this issueBind traits before parent class #15878on Oct 16, 2024
  7. Renji-FR commented on Nov 14, 2024

    @Renji-FR
    Author

    What is the next step to have the fix in the next minor release please?

  8. iluuu1994 commented on Nov 14, 2024

    @iluuu1994
    Member

    Nothing on your side. I need to think through the consequences of my patch when I have the time.

  9. added theissue type on Feb 5, 2025
  10. added a commit that references this issue on Mar 27, 2025
    00b7f83
  11. added a commit that references this issue on Apr 1, 2025
    011795b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions