Skip to content

Conversation

@EricWF
Copy link
Member

@EricWF EricWF commented Aug 2, 2025

No description provided.

EricWF added 30 commits June 19, 2024 14:09
The template stuff is a bit of a hack. I'm considering simply inserting
the statements into the function body and letting the existing code
handle the rest.

Also add some cheaky diagnostics, fix a few name lookup bugs, and
delete some old code.
There are also various experiements that need cleaning up,
specifically around builtins and source location.
Some work on constification, but a bunch of experiment that still needs
to be undone.

The addition of -fclang-contract-groups=foo=<semantic>, which still
needs better integrating with -fcontract-evaluation-semantic=.

The [[clang::contract_group("foo.bar")]] stuff works really well.
I'll be happy to try it in libc++.

What still needs to be done includes:
  * Coming up with a proper descriptor/format for the compiler-stl
    interface. It seems to me we'll want the ABI to be able to evolve
over time, and so we should pass a pointer to data + data descriptor of
some kind so that the STL can parse version of the header from different
compilers & versions.

  * Everything to do with parsing pre/post in inline member functions.
  * Contract redeclaration checking.
  * Constifying implicit this.
  * Code gen & constant evaluation for result names (and representation
    too).
  * Cleanup around experiments for source location & constification.
The template stuff is a bit of a hack. I'm considering simply inserting
the statements into the function body and letting the existing code
handle the rest.

Also add some cheaky diagnostics, fix a few name lookup bugs, and
delete some old code.
There are also various experiements that need cleaning up,
specifically around builtins and source location.
Some work on constification, but a bunch of experiment that still needs
to be undone.

The addition of -fclang-contract-groups=foo=<semantic>, which still
needs better integrating with -fcontract-evaluation-semantic=.

The [[clang::contract_group("foo.bar")]] stuff works really well.
I'll be happy to try it in libc++.

What still needs to be done includes:
  * Coming up with a proper descriptor/format for the compiler-stl
    interface. It seems to me we'll want the ABI to be able to evolve
over time, and so we should pass a pointer to data + data descriptor of
some kind so that the STL can parse version of the header from different
compilers & versions.

  * Everything to do with parsing pre/post in inline member functions.
  * Contract redeclaration checking.
  * Constifying implicit this.
  * Code gen & constant evaluation for result names (and representation
    too).
  * Cleanup around experiments for source location & constification.
This allows better versioning, and also prevents us from clobbering
every registery. (though that latter case is likely not too impactful).
Also, re-enable the exception handling tentatively.
Copy link

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am far from having done a complete pass, but here are some comments.
Chopping this in manageable pieces will be necessary I'm afraid, Github can barely render the PR and reviewers will be discouraged by the sheer size of it all.

Changes to source location could be a PR and then maybe we want to start with just contract_assert, even if i understand it would be annoying for you.

Non essentials (ast matchers for examples) can be separate PRs.
Everything lamba related can also be separated out.

I will keep reviewing today or tomorrow


// Decl used to define the datastructure for the contract violation object
// used for C++ contracts
mutable Decl *BuiltinContractViolationRecordDecl = nullptr;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to make that a RecordDecl directly?

Comment on lines 161 to +162
if (isa<DeclStmt, GenericSelectionExpr, RequiresExpr,
OpenACCWaitConstruct, SYCLKernelCallStmt>(S))
OpenACCWaitConstruct, SYCLKernelCallStmt, ContractStmt>(S))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should just make expressions children. It's not yet clear to me why they should not be, as I do a linear read through

/// int foo() post(r : r > 0);
///
/// Where `r` refers to the value returned by the function
class ResultNameDecl : public ValueDecl {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be called ContractXXXX. Maybe ContractNamedReturnDecl

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result name decl is what's used in the spec, but I'm happy to add Contract to it.

// qualifier, to be used for the (uncommon) case of out-of-line declarations
// and constrained function decls.
// qualifier, to be used for the (uncommon) case of out-of-line declarations,
// constrained function decls or functions with contracts.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// constrained function decls or functions with contracts.
// constrained function decls or functions with pre/post conditions.

// which are heavily based on AltiVec
// KEYBORLAND - This is a keyword if Borland extensions are enabled
// KEYCOROUTINES - This is a keyword if support for C++ coroutines is enabled
// KEYCONTRACTS - This is a keyword if support for C++ contracts is enabled
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the upstream version I think

  • we want to put contracts behind the C++26 flags
  • allow them in all c++ versions (without flag) once they are a bit baked

Comment on lines +1630 to +1636
// C++ Contracts
defm contracts : BoolFOption<"contracts",
LangOpts<"Contracts">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option, CLOption],
"Enable support for C++ Contracts">,
NegFlag<SetFalse, [], [ClangOption, CC1Option]>>;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cf above, lets forgo that for now

Comment on lines +1646 to +1656
def fcontract_group_evaluation_semantic_EQ : CommaJoined<["-"], "fcontract-group-evaluation-semantic=">,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Clang extension. Enable or disable contracts by group. The argument is a comma-separated "
"sequence of one or more group names, each prefixed by '+' or '-'.">;

defm contract_exceptions : BoolFOption<"contract-exceptions",
LangOpts<"ContractExceptions">, DefaultTrue,
PosFlag<SetTrue, [], [ClangOption],
"Enable contract violation on exception">,
NegFlag<SetFalse, [], [ClangOption, CC1Option],
"Disable contract violations on exception">>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do that in a subsequent PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we can certainly do that. It was useful for testing.

Comment on lines +8958 to +8972
std::optional<ContractKind> getContractKeyword(const Token &Token) const;
std::optional<ContractKind> getContractKeyword() const {
return getContractKeyword(Tok);
}
bool isFunctionContractKeyword() const {
return isFunctionContractKeyword(Tok);
}
bool isFunctionContractKeyword(const Token &Token) const {
return getContractKeyword(Token).value_or(ContractKind::Assert) !=
ContractKind::Assert;
}

bool isAnyContractKeyword(const Token &Token) const {
return getContractKeyword(Token).has_value();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cf above, lets keep assert separate from pre/post

/// ScopeFlags - These are bitfields that are or'd together when creating a
/// scope, which defines the sorts of things the scope contains.
enum ScopeFlags {
enum ScopeFlags : unsigned long {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we want to introduce an alias for the underlying type

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Comment on lines +110 to +116
enum class ContractEmissionStyle {
/// Emit the contract at every call site, in the caller.
InCaller,

/// Emit the contract with the function definition.
InDefinition,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we focus on callee side initially?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I should remove the emission style stuff since it's not implemented.

Comment on lines +121 to +125
enum ContractScopeOffset {
CSO_ParentContext,
CSO_FunctionContext
};

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets use a scoped enum here

"woops... FIXME(EricWF): %0"
>, InGroup<EricWFFixme>;

def err_result_name_shadows_param : Error<
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be more consistent about using _contract_ in these diags?

def err_result_name_shadows_param : Error<
"declaration of result name %0 shadows parameter">;
def err_void_result_name : Error<
"result name %0 cannot be used with a void return type">;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used? declared? introduced?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"result name in the post condition of a function with a void return type" maybe

Comment on lines +12595 to +12603
def err_contract_violation : Error<"contract %select{precondition|postcondition|assertion}0 failed%select{: %2|}1">;
def warn_contract_violation : Warning<"contract %select{precondition|postcondition|assertion}0 failed%select{: %2|}1">,
InGroup<ContractViolation>;

def err_contract_requirement_failed : Error<
"%select{precondition|postcondition|contract assertion}0 failed due to requirement '%1'%select{: %3|}2">;
def warn_contract_requirement_failed : Warning<
"%select{precondition|postcondition|contract assertion}0 failed due to requirement '%1'%select{: %3|}2">,
InGroup<ContractViolation>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of these are (nor can) ever be used, right?

Comment on lines +290 to +291
ContractSpecifierDecl *
Parser::ParseLexedFunctionContractsInScope(CachedTokens &ContractToks,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not anymore I think. It was left over. Removing.

Comment on lines +190 to +192
ContractSpecifierDecl *Seq = Actions.ActOnFinishContractSpecifierSequence(
Contracts, StartLoc, IsInvalid);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should check we are parsing a function (and not a type or a function pointer)
see err_requires_clause_on_declarator_not_declaring_a_function

Comment on lines +238 to +240
// FIXME(EricWF): We allow parsing the result name declarator in `pre` so we
// can diagnose it but we don't do the same for contract assert... Should we?
if ((CK != ContractKind::Assert) && Tok.is(tok::identifier) &&
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should not!

Comment on lines +274 to +280
if (Cond.isInvalid()) {
Cond =
Actions.CreateRecoveryExpr(ExprLoc, EndLoc, {}, Actions.Context.BoolTy);
} else {
SetInvalidOnExit.release();
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here for the braces

EricWF pushed a commit that referenced this pull request Aug 7, 2025
…erver (llvm#148774)

Summary:
There was a deadlock was introduced by [PR
llvm#146441](llvm#146441) which changed
`CurrentThreadIsPrivateStateThread()` to
`CurrentThreadPosesAsPrivateStateThread()`. This change caused the
execution path in
[`ExecutionContextRef::SetTargetPtr()`](https://github.com/llvm/llvm-project/blob/10b5558b61baab59c7d3dff37ffdf0861c0cc67a/lldb/source/Target/ExecutionContext.cpp#L513)
to now enter a code block that was previously skipped, triggering
[`GetSelectedFrame()`](https://github.com/llvm/llvm-project/blob/10b5558b61baab59c7d3dff37ffdf0861c0cc67a/lldb/source/Target/ExecutionContext.cpp#L522)
which leads to a deadlock.

Thread 1 gets m_modules_mutex in
[`ModuleList::AppendImpl`](https://github.com/llvm/llvm-project/blob/96148f92146e5211685246722664e51ec730e7ba/lldb/source/Core/ModuleList.cpp#L218),
Thread 3 gets m_language_runtimes_mutex in
[`GetLanguageRuntime`](https://github.com/llvm/llvm-project/blob/96148f92146e5211685246722664e51ec730e7ba/lldb/source/Target/Process.cpp#L1501),
but then Thread 1 waits for m_language_runtimes_mutex in
[`GetLanguageRuntime`](https://github.com/llvm/llvm-project/blob/96148f92146e5211685246722664e51ec730e7ba/lldb/source/Target/Process.cpp#L1501)
while Thread 3 waits for m_modules_mutex in
[`ScanForGNUstepObjCLibraryCandidate`](https://github.com/llvm/llvm-project/blob/96148f92146e5211685246722664e51ec730e7ba/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp#L57).

This fixes the deadlock by adding a scoped block around the mutex lock
before the call to the notifier, and moved the notifier call outside of
the mutex-guarded section. The notifier call
[`NotifyModuleAdded`](https://github.com/llvm/llvm-project/blob/96148f92146e5211685246722664e51ec730e7ba/lldb/source/Target/Target.cpp#L1810)
should be thread-safe, since the module should be added to the
`ModuleList` before the mutex is released, and the notifier doesn't
modify the module list further, and the call is operates on local state
and the `Target` instance.

### Deadlocked Thread backtraces:
```
* thread #3, name = 'dbg.evt-handler', stop reason = signal SIGSTOP
  * frame #0: 0x00007f2f1e2973dc libc.so.6`futex_wait(private=0, expected=2, futex_word=0x0000563786bd5f40) at    futex-internal.h:146:13
   /*... a bunch of mutex related bt ... */    
   liblldb.so.21.0git`std::lock_guard<std::recursive_mutex>::lock_guard(this=0x00007f2f0f1927b0, __m=0x0000563786bd5f40) at std_mutex.h:229:19
    frame #8: 0x00007f2f27946eb7 liblldb.so.21.0git`ScanForGNUstepObjCLibraryCandidate(modules=0x0000563786bd5f28, TT=0x0000563786bd5eb8) at GNUstepObjCRuntime.cpp:60:41
    frame #9: 0x00007f2f27946c80 liblldb.so.21.0git`lldb_private::GNUstepObjCRuntime::CreateInstance(process=0x0000563785e1d360, language=eLanguageTypeObjC) at GNUstepObjCRuntime.cpp:87:8
    frame #10: 0x00007f2f2746fca5 liblldb.so.21.0git`lldb_private::LanguageRuntime::FindPlugin(process=0x0000563785e1d360, language=eLanguageTypeObjC) at LanguageRuntime.cpp:210:36
    frame #11: 0x00007f2f2742c9e3 liblldb.so.21.0git`lldb_private::Process::GetLanguageRuntime(this=0x0000563785e1d360, language=eLanguageTypeObjC) at Process.cpp:1516:9
    ...
    frame llvm#21: 0x00007f2f2750b5cc liblldb.so.21.0git`lldb_private::Thread::GetSelectedFrame(this=0x0000563785e064d0, select_most_relevant=DoNoSelectMostRelevantFrame) at Thread.cpp:274:48
    frame llvm#22: 0x00007f2f273f9957 liblldb.so.21.0git`lldb_private::ExecutionContextRef::SetTargetPtr(this=0x00007f2f0f193778, target=0x0000563786bd5be0, adopt_selected=true) at ExecutionContext.cpp:525:32
    frame llvm#23: 0x00007f2f273f9714 liblldb.so.21.0git`lldb_private::ExecutionContextRef::ExecutionContextRef(this=0x00007f2f0f193778, target=0x0000563786bd5be0, adopt_selected=true) at ExecutionContext.cpp:413:3
    frame llvm#24: 0x00007f2f270e80af liblldb.so.21.0git`lldb_private::Debugger::GetSelectedExecutionContext(this=0x0000563785d83bc0) at Debugger.cpp:1225:23
    frame llvm#25: 0x00007f2f271bb7fd liblldb.so.21.0git`lldb_private::Statusline::Redraw(this=0x0000563785d83f30, update=true) at Statusline.cpp:136:41
    ...
* thread #1, name = 'lldb', stop reason = signal SIGSTOP
  * frame #0: 0x00007f2f1e2973dc libc.so.6`futex_wait(private=0, expected=2, futex_word=0x0000563785e1dd98) at futex-internal.h:146:13
   /*... a bunch of mutex related bt ... */    
   liblldb.so.21.0git`std::lock_guard<std::recursive_mutex>::lock_guard(this=0x00007ffe62be0488, __m=0x0000563785e1dd98) at std_mutex.h:229:19
    frame #8: 0x00007f2f2742c8d1 liblldb.so.21.0git`lldb_private::Process::GetLanguageRuntime(this=0x0000563785e1d360, language=eLanguageTypeC_plus_plus) at Process.cpp:1510:41
    frame #9: 0x00007f2f2743c46f liblldb.so.21.0git`lldb_private::Process::ModulesDidLoad(this=0x0000563785e1d360, module_list=0x00007ffe62be06a0) at Process.cpp:6082:36
    ...
    frame llvm#13: 0x00007f2f2715cf03 liblldb.so.21.0git`lldb_private::ModuleList::AppendImpl(this=0x0000563786bd5f28, module_sp=ptr = 0x563785cec560, use_notifier=true) at ModuleList.cpp:246:19
    frame llvm#14: 0x00007f2f2715cf4c liblldb.so.21.0git`lldb_private::ModuleList::Append(this=0x0000563786bd5f28, module_sp=ptr = 0x563785cec560, notify=true) at ModuleList.cpp:251:3
    ...
    frame llvm#19: 0x00007f2f274349b3 liblldb.so.21.0git`lldb_private::Process::ConnectRemote(this=0x0000563785e1d360, remote_url=(Data = "connect://localhost:1234", Length = 24)) at Process.cpp:3250:9
    frame llvm#20: 0x00007f2f27411e0e liblldb.so.21.0git`lldb_private::Platform::DoConnectProcess(this=0x0000563785c59990, connect_url=(Data = "connect://localhost:1234", Length = 24), plugin_name=(Data = "gdb-remote", Length = 10), debugger=0x0000563785d83bc0, stream=0x00007ffe62be3128, target=0x0000563786bd5be0, error=0x00007ffe62be1ca0) at Platform.cpp:1926:23
```

## Test Plan:
Built a hello world a.out
Run server in one terminal:
```
~/llvm/build/Debug/bin/lldb-server g :1234 a.out
```
Run client in another terminal
```
~/llvm/build/Debug/bin/lldb -o "gdb-remote 1234" -o "b hello.cc:3"
```

Before:
Client hangs indefinitely
```
~/llvm/build/Debug/bin/lldb -o "gdb-remote 1234" -o "b main"
(lldb) gdb-remote 1234

^C^C
```

After:
```
~/llvm/build/Debug/bin/lldb -o "gdb-remote 1234" -o "b hello.cc:3"
(lldb) gdb-remote 1234
Process 837068 stopped
* thread #1, name = 'a.out', stop reason = signal SIGSTOP
    frame #0: 0x00007ffff7fe4a60
ld-linux-x86-64.so.2`_start:
->  0x7ffff7fe4a60 <+0>: movq   %rsp, %rdi
    0x7ffff7fe4a63 <+3>: callq  0x7ffff7fe5780 ; _dl_start at rtld.c:522:1

ld-linux-x86-64.so.2`_dl_start_user:
    0x7ffff7fe4a68 <+0>: movq   %rax, %r12
    0x7ffff7fe4a6b <+3>: movl   0x18067(%rip), %eax ; _dl_skip_args
(lldb) b hello.cc:3
Breakpoint 1: where = a.out`main + 15 at hello.cc:4:13, address = 0x00005555555551bf
(lldb) c
Process 837068 resuming
Process 837068 stopped
* thread #1, name = 'a.out', stop reason = breakpoint 1.1
    frame #0: 0x00005555555551bf a.out`main at hello.cc:4:13
   1   	#include <iostream>
   2
   3   	int main() {
-> 4   	  std::cout << "Hello World" << std::endl;
   5   	  return 0;
   6   	}
```
EricWF added 6 commits August 7, 2025 12:55
Resolved merge conflicts by keeping both sides of changes:
- clang/include/clang/AST/Decl.h: Added both ContractKind enum and UsualDeleteParams struct
- clang/lib/AST/DeclPrinter.cpp: Added both VisitResultNameDecl and VisitTemplateTemplateParmDecl functions
- clang/lib/Frontend/CompilerInvocation.cpp: Added both contract group serialization and AllocToken code
EricWF pushed a commit that referenced this pull request Nov 18, 2025
…am (llvm#167724)

This got exposed by `09262656f32ab3f2e1d82e5342ba37eecac52522`.

The underlying stream of `m_os` is referenced by the `TextDiagnostic`
member of `TextDiagnosticPrinter`. It got turned into a
`llvm::formatted_raw_ostream` in the commit above. When
`~TextDiagnosticPrinter` (and thus `~TextDiagnostic`) is invoked, we now
call `~formatted_raw_ostream`, which tries to access the underlying
stream. But `m_os` was already deleted because it is earlier in the
order of destruction in `TextDiagnosticPrinter`. Move the `m_os` member
before the `TextDiagnosticPrinter` to avoid a use-after-free.

Drive-by:
* Also move the `m_output` member which the `m_os` holds a reference to.
The fact it's a reference indicates the expectation is most likely that
the string outlives the stream.

The ASAN macOS bot is currently failing with this:
```
08:15:39  =================================================================
08:15:39  ==61103==ERROR: AddressSanitizer: heap-use-after-free on address 0x60600012cf40 at pc 0x00012140d304 bp 0x00016eecc850 sp 0x00016eecc848
08:15:39  READ of size 8 at 0x60600012cf40 thread T0
08:15:39      #0 0x00012140d300 in llvm::formatted_raw_ostream::releaseStream() FormattedStream.h:205
08:15:39      #1 0x00012140d3a4 in llvm::formatted_raw_ostream::~formatted_raw_ostream() FormattedStream.h:145
08:15:39      #2 0x00012604abf8 in clang::TextDiagnostic::~TextDiagnostic() TextDiagnostic.cpp:721
08:15:39      #3 0x00012605dc80 in clang::TextDiagnosticPrinter::~TextDiagnosticPrinter() TextDiagnosticPrinter.cpp:30
08:15:39      #4 0x00012605dd5c in clang::TextDiagnosticPrinter::~TextDiagnosticPrinter() TextDiagnosticPrinter.cpp:27
08:15:39      #5 0x0001231fb210 in (anonymous namespace)::StoringDiagnosticConsumer::~StoringDiagnosticConsumer() ClangModulesDeclVendor.cpp:47
08:15:39      #6 0x0001231fb3bc in (anonymous namespace)::StoringDiagnosticConsumer::~StoringDiagnosticConsumer() ClangModulesDeclVendor.cpp:47
08:15:39      #7 0x000129aa9d70 in clang::DiagnosticsEngine::~DiagnosticsEngine() Diagnostic.cpp:91
08:15:39      #8 0x0001230436b8 in llvm::RefCountedBase<clang::DiagnosticsEngine>::Release() const IntrusiveRefCntPtr.h:103
08:15:39      #9 0x0001231fe6c8 in (anonymous namespace)::ClangModulesDeclVendorImpl::~ClangModulesDeclVendorImpl() ClangModulesDeclVendor.cpp:93
08:15:39      #10 0x0001231fe858 in (anonymous namespace)::ClangModulesDeclVendorImpl::~ClangModulesDeclVendorImpl() ClangModulesDeclVendor.cpp:93
...
08:15:39
08:15:39  0x60600012cf40 is located 32 bytes inside of 56-byte region [0x60600012cf20,0x60600012cf58)
08:15:39  freed by thread T0 here:
08:15:39      #0 0x0001018abb88 in _ZdlPv+0x74 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x4bb88)
08:15:39      #1 0x0001231fb1c0 in (anonymous namespace)::StoringDiagnosticConsumer::~StoringDiagnosticConsumer() ClangModulesDeclVendor.cpp:47
08:15:39      #2 0x0001231fb3bc in (anonymous namespace)::StoringDiagnosticConsumer::~StoringDiagnosticConsumer() ClangModulesDeclVendor.cpp:47
08:15:39      #3 0x000129aa9d70 in clang::DiagnosticsEngine::~DiagnosticsEngine() Diagnostic.cpp:91
08:15:39      #4 0x0001230436b8 in llvm::RefCountedBase<clang::DiagnosticsEngine>::Release() const IntrusiveRefCntPtr.h:103
08:15:39      #5 0x0001231fe6c8 in (anonymous namespace)::ClangModulesDeclVendorImpl::~ClangModulesDeclVendorImpl() ClangModulesDeclVendor.cpp:93
08:15:39      #6 0x0001231fe858 in (anonymous namespace)::ClangModulesDeclVendorImpl::~ClangModulesDeclVendorImpl() ClangModulesDeclVendor.cpp:93
...
08:15:39
08:15:39  previously allocated by thread T0 here:
08:15:39      #0 0x0001018ab760 in _Znwm+0x74 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x4b760)
08:15:39      #1 0x0001231f8dec in lldb_private::ClangModulesDeclVendor::Create(lldb_private::Target&) ClangModulesDeclVendor.cpp:732
08:15:39      #2 0x00012320af58 in lldb_private::ClangPersistentVariables::GetClangModulesDeclVendor() ClangPersistentVariables.cpp:124
08:15:39      #3 0x0001232111f0 in lldb_private::ClangUserExpression::PrepareForParsing(lldb_private::DiagnosticManager&, lldb_private::ExecutionContext&, bool) ClangUserExpression.cpp:536
08:15:39      #4 0x000123213790 in lldb_private::ClangUserExpression::Parse(lldb_private::DiagnosticManager&, lldb_private::ExecutionContext&, lldb_private::ExecutionPolicy, bool, bool) ClangUserExpression.cpp:647
08:15:39      #5 0x00012032b258 in lldb_private::UserExpression::Evaluate(lldb_private::ExecutionContext&, lldb_private::EvaluateExpressionOptions const&, llvm::StringRef, llvm::StringRef, std::__1::shared_ptr<lldb_private::ValueObject>&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*, lldb_private::ValueObject*) UserExpression.cpp:280
08:15:39      #6 0x000120724010 in lldb_private::Target::EvaluateExpression(llvm::StringRef, lldb_private::ExecutionContextScope*, std::__1::shared_ptr<lldb_private::ValueObject>&, lldb_private::EvaluateExpressionOptions const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*, lldb_private::ValueObject*) Target.cpp:2905
08:15:39      #7 0x00011fc7bde0 in lldb::SBTarget::EvaluateExpression(char const*, lldb::SBExpressionOptions const&) SBTarget.cpp:2305
08:15:39  ==61103==ABORTING
...
```
EricWF and others added 10 commits November 18, 2025 15:26
Resolved merge conflicts:
- clang/lib/Basic/IdentifierTable.cpp: Kept enum definitions with KEYCONTRACTS support
- libcxx/utils/libcxx/test/features.py: Kept modified version (modify-delete conflict)
After merging contracts-base into contracts-nightly, several build errors
needed to be addressed:

1. TokenKey and KeywordStatus enums were moved to IdentifierTable.h in
   contracts-base, but contracts-nightly had added KEYCONTRACTS to the
   .cpp file version. Added KEYCONTRACTS to the header and removed the
   duplicate enum definitions from the .cpp file.

2. ArrayRef no longer accepts std::nullopt for construction in newer LLVM.
   Updated return statements to use {} instead of std::nullopt in:
   - FunctionDecl::contracts() in Decl.cpp
   - Sema::getInterveningContractScopes() in SemaContract.cpp
Co-authored-by: Corentin Jabot <corentinjabot@gmail.com>
Co-authored-by: Corentin Jabot <corentinjabot@gmail.com>
Co-authored-by: Corentin Jabot <corentinjabot@gmail.com>
EricWF pushed a commit that referenced this pull request Dec 29, 2025
…lvm#159480)

When building rustc std for arm64e, core fails to compile successfully
with the error:
```
Constant ValueID not recognized.
UNREACHABLE executed at rust/src/llvm-project/llvm/lib/Transforms/Utils/FunctionComparator.cpp:523!
```

This is a result of function merging so I modified
FunctionComparator.cpp as the ConstantPtrAuth value would go unchecked
in the switch statement.

The test case is a reduction from the failure in core and fails on main
with:
```
********************
FAIL: LLVM :: Transforms/MergeFunc/ptrauth-const-compare.ll (59809 of 59995)
******************** TEST 'LLVM :: Transforms/MergeFunc/ptrauth-const-compare.ll' FAILED ********************
Exit Code: 2

Command Output (stdout):
--
# RUN: at line 3
/Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc < /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll | /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# executed command: /Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc
# .---command stderr------------
# | Constant ValueID not recognized.
# | UNREACHABLE executed at /Users/oskarwirga/llvm-project/llvm/lib/Transforms/Utils/FunctionComparator.cpp:523!
# | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
# | Stack dump:
# | 0.	Program arguments: /Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc
# | 1.	Running pass "mergefunc" on module "<stdin>"
# |  #0 0x0000000103335770 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102651770)
# |  #1 0x00000001033336bc llvm::sys::RunSignalHandlers() (/Users/oskarwirga/llvm-project/build/bin/opt+0x10264f6bc)
# |  #2 0x0000000103336218 SignalHandler(int, __siginfo*, void*) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102652218)
# |  #3 0x000000018e6c16a4 (/usr/lib/system/libsystem_platform.dylib+0x1804ad6a4)
# |  #4 0x000000018e68788c (/usr/lib/system/libsystem_pthread.dylib+0x18047388c)
# |  #5 0x000000018e590a3c (/usr/lib/system/libsystem_c.dylib+0x18037ca3c)
# |  #6 0x00000001032a84bc llvm::install_out_of_memory_new_handler() (/Users/oskarwirga/llvm-project/build/bin/opt+0x1025c44bc)
# |  #7 0x00000001033b37c0 llvm::FunctionComparator::cmpMDNode(llvm::MDNode const*, llvm::MDNode const*) const (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026cf7c0)
# |  #8 0x00000001033b4d90 llvm::FunctionComparator::cmpBasicBlocks(llvm::BasicBlock const*, llvm::BasicBlock const*) const (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026d0d90)
# |  #9 0x00000001033b5234 llvm::FunctionComparator::compare() (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026d1234)
# | #10 0x0000000102d6d868 (anonymous namespace)::MergeFunctions::insert(llvm::Function*) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102089868)
# | #11 0x0000000102d6bc0c llvm::MergeFunctionsPass::runOnModule(llvm::Module&) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102087c0c)
# | llvm#12 0x0000000102d6b430 llvm::MergeFunctionsPass::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102087430)
# | llvm#13 0x0000000102b90558 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/Users/oskarwirga/llvm-project/build/bin/opt+0x101eac558)
# | llvm#14 0x0000000103734bc4 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::__1::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool, bool) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102a50bc4)
# | llvm#15 0x000000010373cc28 optMain (/Users/oskarwirga/llvm-project/build/bin/opt+0x102a58c28)
# | llvm#16 0x000000018e2e6b98
# `-----------------------------
# error: command failed with exit status: -6
# executed command: /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# .---command stderr------------
# | FileCheck error: '<stdin>' is empty.
# | FileCheck command line:  /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# `-----------------------------
# error: command failed with exit status: 2
```
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.

3 participants