Skip to content

Make SUCCESS & FAILURE result codes strongly typed to avoid conflicts - #152

Closed
furiousant wants to merge 3 commits into
php:masterfrom
furiousant:master
Closed

furiousant wants to merge 3 commits into
php:masterfrom
furiousant:master

Conversation

@furiousant

Copy link
Copy Markdown

Currently SUCCESS & FAILURE codes are macros. These are reasonably common symbol names, and when integrating PHP with a large code base then conflicts can occur when the macros leak out of the local scope.

Stuart Langley added 3 commits August 7, 2012 13:06
symbol names and using #defines can cause conflicts.
* 5.3:
  Strong types for the result codes SUCCESS & FAILURE. These are common symbol names and using #defines can cause conflicts.
* 5.4:
  Strong types for the result codes SUCCESS & FAILURE. These are common symbol names and using #defines can cause conflicts.
@php-pulls

Copy link
Copy Markdown

Comment on behalf of lstrojny at php.net:

Merged into PHP-5.4 and master. Thanks!

@php-pulls php-pulls closed this Aug 26, 2012
heyJordanParker referenced this pull request in heyJordanParker/php-sharp Oct 12, 2026
… to Mago c314bab

Problem: PR #102 must be up to date with master. Master gained #103, which keeps a
PHP# change to a static List, Map or Set. It also gained #104 and #105, which
make the spec guard fail only when an example joins sharp/baseline/spec, and
make the list name only the examples that fail. Mago #152 merged its own master
and moved to c314bab.

Solution: merge master. Git merged every file on its own, the generated
Zend/zend_vm_execute.h included. zend_vm_gen.php regenerates the same VM files
from the merged zend_vm_def.h. #103 marks ZEND_FETCH_STATIC_PROP_R with
ZEND_SHARP_OPERATOR in extended_value, and this branch's
ZEND_SHARP_VERIFY_TYPE_ARGS keeps its own flag on its own opcode.

The pin moves to Mago c314bab. Only SHARP_MAGO_COMMIT changes in
ext/sharp/sharp_unit.h, and SHARP_UNIT_ABI stays the same. method-declarations,
generic-method-inference, self-on-type-parameters and index-rules now pass and
leave sharp/baseline/spec. sharp/bin/test passes 199 of 199 tests without
opcache, in both file cache modes, twice in one process, under each of the 16
optimizer passes alone, and under the tracing and function JITs. The census
and sharp/bin/test --spec pass.

.
├── Zend/                       master's static receiver beside this branch's checks
├── ext/opcache/jit/            master's inline static object under the JIT
├── ext/sharp/
│   ├── php_sharp.h, sharp.c    master's sharp_collection_of_static_property
│   └── sharp_unit.h            pinned to Mago c314bab
├── sharp/baseline/spec         four examples that now pass leave the list
└── sharp/bin/spec, docs/       master's guard and its documents
heyJordanParker referenced this pull request in heyJordanParker/php-sharp Oct 12, 2026
Problem: a decided feature ships whole. A non-private static field of a PHP#
generic class type is part of spec section 11, so a plain PHP write of the
wrong type arguments must throw the TypeError an instance property's `set` hook
throws. PHP gives a static property no hooks, so Mago refused such a property
as "not supported yet".

Solution: PHP already checks a typed static property's type on every write, so
that check also checks the type arguments.
- zend_property_info gains sharp_type, the property's PHP# type text, kept,
  freed and persisted by opcache like doc_comment.
- zend_compile_prop_group reads it from a ZEND_AST_SHARP_TYPE_ARGS around the
  property's type, which Mago writes for a static property plain PHP can write.
- i_zend_check_property_type, for an assignment, and
  i_zend_verify_type_assignable_zval, for a reference, call
  sharp_property_accepts once an object passes PHP's class check. A write from a
  .sharp file is never checked, as a set hook's value is not.
- The TypeError reads as the instance property's: "Cannot assign
  App.PaginatedList<Checks.Customer> to property Checks\Shelf::$latest of type
  App.PaginatedList<App.Order>?", and "to reference held by property" for a
  reference.
- SHARP_MAGO_COMMIT pins Mago 7951b2c (#152), whose bridge writes the type
  text and which merges Mago master d3adf51.

Zend/
├── zend_compile.h                 sharp_type on zend_property_info
├── zend_ast.h                     ZEND_AST_SHARP_TYPE_ARGS as a property's type
├── zend_compile.c                 zend_compile_prop_group stores the text
├── zend_API.c                     zend_declare_typed_property starts it NULL
├── zend_opcode.c                  destroy_zend_class frees it
├── zend_execute.c                 both checks, both TypeErrors
└── tests/sharp/
    ├── TypeArgumentsChecks.sharp       Shelf.latest, Shelf.kept, Shelf.keepBoth
    └── type_arguments_entry_check.phpt plain PHP, subclass, reflection and reference writes throw; right writes pass
ext/
├── opcache/zend_persist.c         stores the text
├── opcache/zend_persist_calc.c    counts it
├── opcache/zend_file_cache.c      serializes it
├── sharp/php_sharp.h              sharp_property_accepts, sharp_property_type_error
├── sharp/sharp.c                  both
└── sharp/sharp_unit.h             SHARP_MAGO_COMMIT
docs/decisions/072-reified-generics.md   a static property's write is checked
heyJordanParker referenced this pull request in heyJordanParker/php-sharp Oct 12, 2026
…uctors, events and init

Mago #152 merged Mago master's named constructors (php#161), events (php#162) and
the `init` accessor (php#169), so this branch pinned a Mago without them, and
the spec examples that use them still failed here.

The engine now builds from Mago 1b517f4. The generated header changes only
in the commit it names. Six more spec examples pass and leave the baseline,
written with the `cp` command `sharp/bin/test --spec` prints:
- §9.1 constructor-call-this, named-constructors and set-once
- §15 extern-event, extern-event-effects and standalone-event

ext/sharp/sharp_unit.h   <- names Mago 1b517f4
sharp/baseline/spec      <- the six examples that now pass leave it
heyJordanParker referenced this pull request in heyJordanParker/php-sharp Oct 13, 2026
Problem: team-lead's Queue 10 fixes generics R3c's walk, which PHP#'s
every check of a value against a type calls:
\Sharp\Internal\accepts(string $type, mixed &$value, string $path,
array &$problems): bool. `parse` checked each type with its own
generated code and wrote each problem through
\Sharp\InvalidFields::expected, a second check beside the walk. The
walk writes the problem text, and takes only a struct object where a
struct is expected.

Solution: `parse` reads each field into a hidden local, builds what the
walk takes only as an object, then calls the walk once with the
field's type text and its key as the path. A nested struct is built
through its own `parse`, element by element inside a collection, and
its problems move under the field's path, where the walk's own problem
for it is dropped. A union of structs takes the first that builds. A
backed enum case is built from its backing value. Once the walk accepts
a value, each `Set` in it holds its elements under their keys. The
per-type readers, the key check and the problem text go, since the
walk does all three. `Types::type_text` writes decision 72's type text
for the types a struct holds; generics R1 and R2 (mago-sharp #152)
spell every other type, and the two meet where #152 merges.

crates/sharp-bridge/
├── src/lower.rs          <- parse_field, parse_build, parse_nested, parse_sets; parse_value, parse_atomic,
│                            parse_enum, parse_struct, parse_collection, parse_key, parse_error, type_name gone
├── src/lower/types.rs    <- Types::type_text
└── tests/lower.rs        <- a_struct_parses_each_field_from_its_key_and_creates_itself_through_its_main_constructor,
                             parse_reads_each_field_as_its_type: each field through the walk
heyJordanParker referenced this pull request in heyJordanParker/php-sharp Oct 13, 2026
…o program reaches

Problem: master's patch-coverage job fails PR #152. Lines the slice adds run in
no test, so a mistake in them would ship unseen. Some of those lines also have
bugs no test caught: a type argument written on a call of a function value a
property holds is silently accepted, and a type test or match in a .sharp file
checks the type arguments of its hints through a second path.

Solution: each uncovered line gets one of three things:

- A test row that proves a rule and fails when the line is mutated.
- Its upstream shape back, where the slice only re-indented upstream code.
- Deletion, where a probe shows no accepted program reaches it.

The fixes, with what the test shows:

- `this.twice<int>(2)` on a function value now reports `excess-template-parameter`
  naming `Closure`. It was silently accepted.
- `Report.pair<int>(1, 2)` reports too few type arguments.
- A type test, an `as` and a match arm read their hints through one
  `analyze_php_shape` loop.
- `localize_member_type` and `forwarded_return_type` keep upstream's
  `if !templates.is_empty() && let TObject::Named` shape.
- `fix_type_arguments` reads the method value's one closure signature with
  `get_single`. A parameter the function type leaves out fixes nothing.
- `bound_example` matches only a class's own type parameter.
- `explain_blocked_substitution` reads missing type arguments as none, so one
  guard handles a value that differs in no type argument.
- The semantics bound refusal takes its note from
  `supported(Place::TypeParameter)`, so a type parameter has one note text.

Deleted, each shown unreachable by a probe or by a checker refusal:

- `template_inference.rs`: the `is_inferable` skip in the `ClassLikeString::Generic`
  arm. The probe output is the same with it off.
- `arguments.rs`: the empty-container coercion early return and
  `is_empty_container_construction`.
- `method_signature.rs`:
  - the method-metadata early return
  - the clearing of intersection members' type arguments in `erase`, since PHP#
    refuses a generic class in an intersection
  - the `iterable` arm of `display_erased`
- `lower.rs`: the keyword-keyword arm of `PhpType::holds`. The checker refuses a
  union of two collections or of two `Self`.

crates/
├── analyzer/
│   ├── src/
│   │   ├── expression/instantiation.rs                `new Self` reads get_this_type's type parameters
│   │   ├── expression/mod.rs                          one type-test hint path
│   │   ├── invocation/
│   │   │   ├── arguments.rs                           empty-container return deleted
│   │   │   ├── template_inference.rs                  is_inferable skip deleted
│   │   │   └── template_result.rs                     upstream shape, function-value type arguments refused
│   │   ├── resolver/{method,property}.rs              upstream localize shape, get_single
│   │   ├── statement/class_like/method_signature.rs   bound_example, erase, display_erased
│   │   ├── statement/mod.rs                           match arm hints through analyze_php_shape
│   │   └── utils/template.rs                          one no-difference guard
│   └── tests/sharp.rs                                 rows that prove each covered line
├── semantics/
│   ├── src/internal/checker/sharp.rs                  one type parameter note
│   └── tests/sharp.rs                                 signature note, collection beside a generic class
└── sharp-bridge/src/
    ├── lower.rs                                       keyword-keyword arm deleted
    └── lower/types.rs                                 type text unit tests
heyJordanParker referenced this pull request in heyJordanParker/php-sharp Oct 13, 2026
Problem: PR #152 must be up to date with master, and master gained php#160, the
List and Map collection methods, after the last merge.

Solution: merge master. Git merged every file on its own. The analyzer,
semantics and bridge suites pass on the merged tree.

crates/
├── analyzer/src/expression/call/method_call.rs  both sides kept
├── analyzer/tests/sharp.rs                      both sides' tests kept
├── names/src/lib.rs                             master's change
└── sharp-bridge/
    ├── src/lower.rs                             both sides kept
    ├── src/lower/types.rs                       both sides kept
    └── tests/lower.rs                           both sides' tests kept
heyJordanParker referenced this pull request in heyJordanParker/php-sharp Oct 13, 2026
Problem: PR #152 must be up to date with master. Master gained php#164, which
leaves `unreachable!` lines out of patch-coverage. It also gained php#163, which
adds List insert, remove and clear, and php#165, which fixes a project with a law
that compiles twice. php#163's `takes_operands` calls
`ComparisonResult::with_sharp_rules(true)`, which this branch replaced in its
#129 merge, so the merged tree did not compile.

Solution: merge master. Git merged every file on its own. `takes_operands` builds
its result with `ComparisonResult::for_dialect(Dialect::Sharp)`, the branch's one
constructor path. Master's `with_sharp_rules(true)` made the same result. The
analyzer, semantics, bridge and names suites and the root compile tests pass on
the merged tree, and clippy reports nothing.

crates/
├── analyzer/src/expression/binary/utils.rs   for_dialect(Dialect::Sharp) in takes_operands
├── analyzer/src/expression/call/method_call.rs  master's search runs after the call's type is known
├── names/src/lib.rs                          master's insert, remove and clear
└── sharp-bridge/                             master's List changes beside this branch's checks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants