-
Notifications
You must be signed in to change notification settings - Fork 171
Errors from imported files #1294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
9507942
to
e549cb0
Compare
std::cerr << diagnostics.render(input, lm, compiler_options); | ||
if (!r2.ok) { | ||
LFORTRAN_ASSERT(diagnostics.has_error()) | ||
// LFORTRAN_ASSERT(diagnostics.has_error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you have to keep this in there. This checks that the diagnostics contain an error message if r2
returned an Error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems one of the possible use could be diagnostics
just containing a note/warning and still throw SemanticAbort()
being called to stop further processing of AST->ASR
.
For example, for the below message there is no error (there is a note only), but the processing of the contents of b.y
still needs to be stopped because we could not import a.py
.
note: Imported here
--> /home/ubaid/OpenSource/lpython/src/bin/../runtime/b.py:1:1
|
1 | from a import i
| ^^^^^^^^^^^^^^^
I think you have to keep this in there. This checks that the diagnostics contain an error message if r2 returned an Error.
It seems throw SemanticAbort()
can halt the current processing of AST->ASR
, and on using it, due the LFORTRAN_ASSERT(diagnostics.has_error())
, it is being compulsory that diagnostics
would be containing an error. Although, in the above b.py
example, it is not containing any error, instead it is containing a note.
Please, could someone possibly share if there is a way to stop processing of AST->ASR
apart from using throw SemanticAbort()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess first, from b import j
will happen, then from a import i
and then finally, i: i32 = 3.142
. So when you get the error in a.py
, diagnostics should know that from b import j
and then from a import i
before i: i32 = 3.142
have happened. More like a stack of diagnostics. Fill the diagnostic with a note when a module import happens because you know you are going to jump into external file. If the external file processes successfully then pop the note from the diagnostics. If an error happens then you already have the information of all the imports that have happened before this error and you can pop them and show to the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this approach! @Shaikh-Ubaid give it a try.
Any doubts please ask.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If an error happens then you already have the information of all the imports
One of the concerns it that if an error happens in a.py
, we need to stop the processing of AST->ASR
for b.py
(given that b.py
imports a.py
). Now for diagnostics of b.py
, we add a note saying Imported here
. My doubt is how do we stop the processing of AST->ASR
of b.py
? If we use throw SemanticAbort()
, then the diagnostics
of b.py
needs to contain an error because of LFORTRAN_ASSERT(diagnostics.has_error())
, but instead it just contains a note.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's what I am imagining of the control flow that happens,
- Control is in
c.py
and you processed,from b import j
. The diagnostics stack (abstract concept, upto you how you implement it) contains thatb
was imported. A recursive call topython_ast_to_asr
is made for processingb.py
. - Now control jumped to
b.py
. Here it processed,from a import i
. The diagnostics stack adds thata
was imported. A recursive call topython_ast_to_asr
is made for processinga.py
. - Control is in
a.py
. Now,i: i32 = 3.142
resulting in a semantic error. Its at this point we first add this error to diagnostics stack and then while rendering diagnostics stack keep popping and displaying to the user. And halt the processing/compilation. No need to go back tob.py
.
I don't know about the implementation details but conceptually and ideally IMO that's how it should be done. Whenever you jump to another file, i.e., make a recursive call to python_ast_to_asr
just store the diagnostics of import statement (which is making you to jump to another file which is being imported) in a conceptual stack and move ahead. If you face an error somewhere deep in a file, you already know from the conceptual stack how you reached there and you can use that info along with current semantic error to show the complete trace.
Make sense to you folks @Shaikh-Ubaid, @certik, @Thirumalai-Shaktivel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plus I will look at your PR later today to understand the details of your issue better. Meanwhile may be just try removing the duplicated note I mentioned about in #1294 (comment).
These error messages in different files must be fixed by implementing the location manager support for multiple files. Then you simply collect the location information in any file into the diagnostics, and then the diagnostics will correctly show error messages across files automatically. |
@certik @czgdp1807 @Shaikh-Ubaid diff --git a/src/libasr/location.h b/src/libasr/location.h
index a38d8175c..0bc85dcf0 100644
@@ -102,7 +102,7 @@ struct LocationManager {
- std::string in_filename;
+ std::vector<std::string> in_filename; pass the LocationManager along with the diagnostics as an argument to Now, if there is an error. We just push_back the diagnostics error. As diagnostics is a vector of diagnostic lpython/src/libasr/diagnostics.h Line 114 in dce819f
lpython/src/libasr/diagnostics.h Lines 122 to 124 in dce819f
then, SemanticAbort() and return back to emit_asr() .
Lines 168 to 172 in dce819f
Here, while rendering the diagnostics. lpython/src/libasr/diagnostics.cpp Lines 67 to 76 in dce819f
For each diagnostic, we can use lm.in_filename.back() to get the start indices of the file and render the current file error message.P.S. I assumed that one diagnostic means one file's error . So, diagnostics contains multiple file's error messages.
|
@Thirumalai-Shaktivel Your comment makes sense to me. Why not just implement it and see the result? Plus the error messages in #1294 (comment) contain the note, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comment in #1294 (comment) and try implementing it?
See https://github.com/lcompilers/lpython/pull/1294/files#r1021755155 as well.
Cool, I will submit another testing PR, that depends on this PR. @Shaikh-Ubaid keep it as a reference. |
I think it should be done like this: diff --git a/src/libasr/location.h b/src/libasr/location.h
index a38d8175c..9a02dd210 100644
--- a/src/libasr/location.h
+++ b/src/libasr/location.h
@@ -94,23 +94,36 @@ struct LocationManager {
//
//
//
- std::vector<uint32_t> out_start; // consecutive intervals in the output code
- std::vector<uint32_t> in_start; // start + size in the original code
- std::vector<uint32_t> in_newlines; // position of all \n in the original code
-
- // For preprocessor (if preprocessor==true).
- // TODO: design a common structure, that also works with #include, that
- // has these mappings for each file
- bool preprocessor = false;
- std::string in_filename;
- uint32_t current_line=0;
- std::vector<uint32_t> out_start0; // consecutive intervals in the output code
- std::vector<uint32_t> in_start0; // start + size in the original code
- std::vector<uint32_t> in_size0; // Size of the `in` interval
- std::vector<uint32_t> interval_type0; // 0 .... 1:1; 1 ... many to many;
- std::vector<uint32_t> in_newlines0; // position of all \n in the original code
-// std::vector<uint32_t> filename_id; // file name for each interval, ID
-// std::vector<std::string> filenames; // filenames lookup for an ID
+ struct FileLocations {
+ std::vector<uint32_t> out_start; // consecutive intervals in the output code
+ std::vector<uint32_t> in_start; // start + size in the original code
+ std::vector<uint32_t> in_newlines; // position of all \n in the original code
+
+ // For preprocessor (if preprocessor==true).
+ // TODO: design a common structure, that also works with #include, that
+ // has these mappings for each file
+ bool preprocessor = false;
+ std::string in_filename;
+ uint32_t current_line=0;
+ std::vector<uint32_t> out_start0; // consecutive intervals in the output code
+ std::vector<uint32_t> in_start0; // start + size in the original code
+ std::vector<uint32_t> in_size0; // Size of the `in` interval
+ std::vector<uint32_t> interval_type0; // 0 .... 1:1; 1 ... many to many;
+ std::vector<uint32_t> in_newlines0; // position of all \n in the original code
+ }
+ std::vector<FileLocations> files;
+ std::vector<uint32_t> file_ends; // position of all ends of files
+ // For a given Location we use the `file_ends` and bisection to determine
+ // the file (index) which the location is from. Then we use this index into
+ // the `files` vector and use `in_newlines` and other information to
+ // determin the line and column inside this file, and the `in_filename`
+ // field to determine the filename. This happens when the diagnostic is
+ // printed. The `pos_to_linecol` function below should be modified to
+ // return line, column and the filename:
+ //
+ // void pos_to_linecol(uint32_t position, uint32_t &line, uint32_t &col,
+ // std::string &filename) const;
+
// Converts a position in the output code to a position in the original code
// Every character in the output code has a corresponding location in the |
@Thirumalai-Shaktivel do you want to give it a shot? @Shaikh-Ubaid if you want, you can focus on the wasm_x86 backend, I think you know what to do there. |
To test this, we simply create files in |
Yup, I will start working on this now. |
I would like to give this another try with the approach in #1294 (comment). |
e549cb0
to
66dd29e
Compare
Currently we are storing all the file names (in (At few places there some unnecessary changes which are not needed but are temporarily present so that the project compiles. We can remove/fix them once we solve the above linear index issue.) |
We need this feature soon. @Shaikh-Ubaid, unfortunately I don't have time right now to supervise the details here. Do you know how to finish it? If not, then let others finish it, and try to work on some other feature which you know how to implement. @czgdp1807 and I discussed this issue a few days ago, so he knows what to do, if needed. |
I think I can, but I need my doubts to be clarified. My doubts at #1218 (comment) (and #1218 (comment) last paragraph) still persist. Without a clear idea, we are not sure if we are heading into the right direction and thus not sure if we our solution is correct.
Even for other features I might get doubts and without their clarification we might get stuck in that feature. |
Please take over in that case. |
Closing as it seems completed in #1297. |
Related: #1218
This attempts to support the situation in #1218 (comment).
a.py
b.py
c.py
Output: