Pointer provenance
- title
- Pointer provenance
- type
- concept
- summary
- Идея о том, что указатель несёт не только адрес, но и привязку к исходному выделению памяти, с которой обязаны считаться компиляторы
- tags
- c, cpp, memory-model, compilers
- sources
- simplified-model-of-fil-c
- created
- 2026-04-18
- updated
- 2026-04-18
- lang
- ru
- translation_of
- pointer-provenance
- source_updated
- 2026-04-18
- translated
- 2026-09-01
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Два указателя могут иметь одинаковый битовый шаблон, но при этом не быть взаимозаменяемыми. Provenance (происхождение) - это дополнительные метаданные ("из какого выделения памяти получен этот указатель?"), которые делают их различимыми в модели памяти языка. Именно это делает недопустимыми некоторые оптимизации компилятора, которые кажутся очевидно корректными на уровне битов, но ломают семантику программы на уровне языка.
Пример nerd-snipe
Если p1 и p2 имеют одинаковый тип, вправе ли компилятор переписать if (p1 == p2) { f(p1); } в if (p1 == p2) { f(p2); }?
На битовом уровне - да, мы ведь только что проверили их равенство. Но с учётом provenance - нет: p1 и p2 могут быть численно равны, но происходить из разных выделений памяти. Передача p2 вместо p1 в f означала бы, что f работает через другой provenance, и любые проверки границ (bounds checks), escape-анализ или рассуждения об алиасинге (aliasing) внутри f относились бы не к тому выделению памяти.
В C и C++ модель памяти настолько неоднозначна, что этот вопрос породил многолетние споры в комитетах по стандартизации. Статья о pointer provenance Ральфа Юнга (Ralf Jung) служит здесь общепринятым первоисточником.
Зачем это компиляторам
Современные компиляторы опираются на правила provenance для обоснования оптимизаций:
- Анализ псевдонимов (alias analysis). Указатели из разных выделений памяти не могут ссылаться на один и тот же объект (alias); компилятор может переупорядочивать операции чтения (load) и записи (store) вокруг них.
- Escape-анализ. Указатель, который никогда не выходит за пределы своего выделения памяти, можно сохранить в регистрах.
- Удаление мёртвых записей (dead store elimination). Записи в память, доступную только через один provenance, можно удалить, как только этот provenance перестаёт существовать.
Каждая из этих оптимизаций строится на уверенности компилятора в том, что одинаковые битовые шаблоны с разным provenance не смешиваются друг с другом.
Почему fil-c делает концепцию наглядной
Большинство реализаций языков отслеживают provenance абстрактно - как свойство абстрактной машины, о которой рассуждает компилятор, стирая его во время выполнения. fil-c делает provenance наблюдаемым в рантайме: каждый указатель спарен с AllocationRecord*, который и является provenance. Два указателя с одинаковыми адресами, но разными записями будут вести себя по-разному, поскольку несут разные границы (bounds) и разную достижимость для GC. Поэтому на вопрос о переписывании кода компилятором Fil-C даёт однозначный ответ: эти два указателя не взаимозаменяемы, так как различаются связанные с ними записи.
Это делает Fil-C удобной ментальной моделью для понимания того, что означает "provenance" в реальной системе, в отличие от того, чем он является в абстрактной машине комитета по стандартизации.
Связанные страницы
- simplified-model-of-fil-c - где подробно описана связка provenance и capability
- fil-c - сам проект
- no-silver-bullet - provenance относится к сущностной сложности (essential complexity) модели памяти C/C++; никакие трюки оптимизации не устраняют её